From 81f7ffc7e713ee630f00e99c0d89f7e7fa37c727 Mon Sep 17 00:00:00 2001 From: lanhuajian Date: Mon, 29 May 2023 10:16:51 +0800 Subject: [PATCH 1/5] [feature] Makes Fair Runtime error reporting more explicit --- fair/lib/src/internal/error_tips.dart | 26 +++- fair/lib/src/internal/stack_trace_detail.dart | 132 ++++++++++++++++++ .../src/internal/warning_dialog_widget.dart | 20 ++- fair/lib/src/render/builder.dart | 31 +++- fair/pubspec.yaml | 1 + 5 files changed, 206 insertions(+), 4 deletions(-) create mode 100644 fair/lib/src/internal/stack_trace_detail.dart diff --git a/fair/lib/src/internal/error_tips.dart b/fair/lib/src/internal/error_tips.dart index c077bfef..a5b6bce5 100644 --- a/fair/lib/src/internal/error_tips.dart +++ b/fair/lib/src/internal/error_tips.dart @@ -4,6 +4,7 @@ * found in the LICENSE file. */ +import 'package:fair/src/internal/stack_trace_detail.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; @@ -15,9 +16,19 @@ class WarningWidget extends StatelessWidget { final String? solution; final dynamic error; final BuildContext? parentContext; + final String? stackTrace; + final Map? errorBlock; - const WarningWidget({Key? key, this.name, this.url, this.error, this.solution,this.parentContext}) + const WarningWidget( + {Key? key, + this.name, + this.url, + this.error, + this.solution, + this.parentContext, + this.stackTrace, + this.errorBlock}) : super(key: key); @override @@ -65,6 +76,19 @@ class WarningWidget extends StatelessWidget { cancelFun: (){ Navigator.pop(parentContext!); }, + stackTraceVisible: stackTrace != null, + viewStackTrace: () { + Navigator.push( + parentContext!, + MaterialPageRoute( + builder: (context) => + StackTraceDetailPage( + stackTrace: stackTrace!, + name: name, + errorJson: errorBlock, + error: error, + ))); + }, ); } ); diff --git a/fair/lib/src/internal/stack_trace_detail.dart b/fair/lib/src/internal/stack_trace_detail.dart new file mode 100644 index 00000000..444b7e48 --- /dev/null +++ b/fair/lib/src/internal/stack_trace_detail.dart @@ -0,0 +1,132 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; + +class StackTraceDetailPage extends StatelessWidget { + final String stackTrace; + final String? name; + final Map? errorJson; + final dynamic error; + + StackTraceDetailPage( + {required this.stackTrace, this.name, this.errorJson, this.error}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('StackTrace'), + leading: InkWell( + child: Icon(Icons.arrow_back), + onTap: () { + Navigator.pop(context); + }, + ), + ), + body: SingleChildScrollView( + child: Column( + children: [ + _stackTraceHeader(), + _stackTraceDetails(), + ], + ), + ), + ); + } + + Widget _stackTraceHeader() => RichText( + text: TextSpan( + text: '══╡ EXCEPTION CAUGHT BY FAIR RUNTIME ╞══\n', + style: TextStyle( + fontSize: 15.0, + color: Colors.black, + ), + children: [ + TextSpan( + text: '\nError Tag:', + style: TextStyle( + fontSize: 15.0, + color: Colors.black, + ), + ), + TextSpan( + text: name, + style: TextStyle( + fontWeight: FontWeight.bold, + background: Paint()..color = Colors.redAccent, + )), + TextSpan( + text: ', while parsing:\n', + style: TextStyle( + fontSize: 15.0, + color: Colors.black, + ), + ), + _buildFormattedJsonSpan(), + TextSpan( + text: '\n\nRuntime Error:\n', + style: TextStyle( + fontSize: 22.0, + color: Colors.black, + ), + ), + TextSpan( + text: '$error', + style: TextStyle( + fontSize: 15.0, + color: Colors.black, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ); + + TextSpan _buildFormattedJsonSpan() { + var encoder = JsonEncoder.withIndent(' '); + final formattedJson = encoder.convert(errorJson); + return TextSpan( + text: formattedJson, + style: TextStyle( + fontSize: 15.0, + color: Colors.black, + fontWeight: FontWeight.bold, + ), + ); + } + + final String PKG_PREFIX = 'package:'; + final String DART_SUFFIX = '.dart'; + + RichText _stackTraceDetails() { + final splitter = LineSplitter(); + final stackTraceLines = splitter.convert(stackTrace); + + final defaultTextStyle = + TextStyle(color: Color(0xff333333), fontWeight: FontWeight.w500); + + final spans = stackTraceLines.map((e) { + if (e.contains(PKG_PREFIX)) { + final span1 = e.substring(0, e.indexOf(PKG_PREFIX)); + final highlightSpan = + e.substring(e.indexOf(PKG_PREFIX), e.lastIndexOf(DART_SUFFIX) + 5); + final span2 = e.substring(e.lastIndexOf(DART_SUFFIX) + 5); + + return TextSpan(text: span1, style: defaultTextStyle, children: [ + TextSpan( + text: highlightSpan, style: TextStyle(color: Color(0xFF3978C4))), + TextSpan(text: span2, style: defaultTextStyle), + TextSpan(text: '\n', style: defaultTextStyle), + ]); + } else { + return TextSpan(text: '$e\n'); + } + }).toList(); + + return RichText( + text: TextSpan( + text: '\nWhen the exception was thrown, this was the stack:\n\n', + children: spans, + style: defaultTextStyle)); + } +} diff --git a/fair/lib/src/internal/warning_dialog_widget.dart b/fair/lib/src/internal/warning_dialog_widget.dart index bf43f1b4..47a61219 100644 --- a/fair/lib/src/internal/warning_dialog_widget.dart +++ b/fair/lib/src/internal/warning_dialog_widget.dart @@ -6,6 +6,8 @@ class DialogWidget extends Dialog { final String? solution ; //是否须要"取消"按钮 final dynamic error ; //错误 void Function()? cancelFun; //取消 + void Function()? viewStackTrace; //查看堆栈 + final bool? stackTraceVisible;//堆栈按钮是否可见 DialogWidget({ @@ -15,6 +17,8 @@ class DialogWidget extends Dialog { this.solution, this.error, this.cancelFun, + this.viewStackTrace, + this.stackTraceVisible, }) : super(key: key); @override @@ -111,9 +115,11 @@ class DialogWidget extends Dialog { var widgets = []; widgets.add(_buildBottomCancelButton()); widgets.add(_buildBottomOnline()); + widgets.add(_buildBottomStackTraceButton()); return Flex( direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.spaceAround, children: widgets, ); } @@ -128,7 +134,7 @@ class DialogWidget extends Dialog { Widget _buildBottomCancelButton() { return Flexible( - fit: FlexFit.tight, + fit: FlexFit.loose, child:InkWell( onTap: this.cancelFun, child: Text('Cancel', style: TextStyle(color: Color(0xff666666))), @@ -136,4 +142,16 @@ class DialogWidget extends Dialog { ); } + Widget _buildBottomStackTraceButton() { + if (stackTraceVisible == null || !stackTraceVisible!) { + return Container(); + } + return Flexible( + fit: FlexFit.loose, + child: InkWell( + onTap: viewStackTrace, + child: Text('StackTrace', style: TextStyle(color: Color(0xFF6888DE))), + ), + ); + } } \ No newline at end of file diff --git a/fair/lib/src/render/builder.dart b/fair/lib/src/render/builder.dart index d1fd30cd..c09150d9 100644 --- a/fair/lib/src/render/builder.dart +++ b/fair/lib/src/render/builder.dart @@ -7,6 +7,8 @@ * found in the LICENSE file. */ +import 'dart:convert'; +import 'dart:developer' as developer; import 'package:fair/fair.dart'; import 'package:fair/src/type.dart'; import 'package:flutter/material.dart'; @@ -159,12 +161,14 @@ class DynamicWidgetBuilder extends DynamicBuilder { return children.asListOf() ?? children; } return block(map, methodMap, context, domain, mapper, name, isWidget); - } catch (e) { + } catch (e, stack) { return WarningWidget( parentContext: context, name: name, error: e, url: bundle, + stackTrace: stack.toString(), + errorBlock: map, solution: "Tag name not supported yet,You need to use the @FairBinding annotation to tag the local Widget component"); } @@ -198,10 +202,33 @@ class DynamicWidgetBuilder extends DynamicBuilder { stack: stack, context: ErrorSummary('while parsing widget of $name, $fun'), )); - throw ArgumentError('name===$name,fun===$fun, error===$e, map===$map'); + + //print StackTrack in console + _dumpErrorToConsole(name, map, e, stack); + + rethrow; } } + void _dumpErrorToConsole(String name, Map map, Object e, StackTrace stack) { + var encoder = JsonEncoder.withIndent(' '); + final formattedJson = encoder.convert(map); + + final errorFormatText = ''' + + ══╡ EXCEPTION CAUGHT BY FAIR RUNTIME ╞══════════════════════════════════════════════════════════════ + Error Tag:$name, while parsing: + +$formattedJson + + $e + + When the exception was thrown, this was the stack: + '''; + + developer.log('', error: errorFormatText, level: 900, stackTrace: stack); + } + W positioned( dynamic paMap, Map? methodMap, BuildContext context, Domain? domain) { var pa = []; diff --git a/fair/pubspec.yaml b/fair/pubspec.yaml index c35686d4..013826d1 100644 --- a/fair/pubspec.yaml +++ b/fair/pubspec.yaml @@ -22,6 +22,7 @@ dependencies: flat_buffers: ^2.0.5 url_launcher: ^6.0.10 http: ^0.13.3 + logging: ^1.2.0 dev_dependencies: flutter_test: From 8cf52cb8485ce7ae6476d933fe18c2e2965e9a4d Mon Sep 17 00:00:00 2001 From: lanhuajian Date: Fri, 9 Jun 2023 09:54:45 +0800 Subject: [PATCH 2/5] [feature] remove useless log --- fair/android/src/main/java/com/wuba/fair/thread/FairTask.java | 1 - fair/lib/src/render/builder.dart | 1 - 2 files changed, 2 deletions(-) diff --git a/fair/android/src/main/java/com/wuba/fair/thread/FairTask.java b/fair/android/src/main/java/com/wuba/fair/thread/FairTask.java index 3a204164..4dc10553 100644 --- a/fair/android/src/main/java/com/wuba/fair/thread/FairTask.java +++ b/fair/android/src/main/java/com/wuba/fair/thread/FairTask.java @@ -11,7 +11,6 @@ public abstract class FairTask implements Runnable { @Override public void run() { - FairLogger.d("当前的线程名称" + Thread.currentThread()); runTask(); diff --git a/fair/lib/src/render/builder.dart b/fair/lib/src/render/builder.dart index c09150d9..ec3b7042 100644 --- a/fair/lib/src/render/builder.dart +++ b/fair/lib/src/render/builder.dart @@ -50,7 +50,6 @@ class DynamicWidgetBuilder extends DynamicBuilder { dynamic convert(BuildContext context, Map map, Map? methodMap, {Domain? domain}) { var name = map[tag]; - print('name:$name'); if (name == null) { return WarningWidget( parentContext: context, From cb9b994cc51b5ce56c771625dad8e328a8ddd7a6 Mon Sep 17 00:00:00 2001 From: lanhuajian Date: Fri, 9 Jun 2023 11:42:18 +0800 Subject: [PATCH 3/5] [feature] supports whether users uglify js files --- compiler/lib/src/post_builder.dart | 20 +++++++++++++++++++- compiler/pubspec.yaml | 7 ++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/compiler/lib/src/post_builder.dart b/compiler/lib/src/post_builder.dart index 3985de84..f8986b69 100644 --- a/compiler/lib/src/post_builder.dart +++ b/compiler/lib/src/post_builder.dart @@ -14,6 +14,7 @@ import 'package:crypto/crypto.dart' show md5; import 'package:fair_compiler/src/state_transfer.dart'; import 'package:path/path.dart' as path; import 'package:fair_dart2js/index.dart' as dart2js; +import 'package:yaml/yaml.dart'; import 'helper.dart' show FlatCompiler, ModuleNameHelper; class ArchiveBuilder extends PostProcessBuilder with FlatCompiler { @@ -56,7 +57,24 @@ class ArchiveBuilder extends PostProcessBuilder with FlatCompiler { print('\u001b[33m [Fair Dart2JS] partPath => ${partPath} \u001b[0m'); if (File(partPath).existsSync()) { try { - var result = await dart2js.convertFile(partPath, true); + var uglify = true; + + var optionsYamlPath = + path.join(Directory.current.path, 'fair_compiler_options.yaml'); + + var optionYamlFile = File(optionsYamlPath); + if (optionYamlFile.existsSync()) { + var optionsYaml = + loadYaml(optionYamlFile.readAsStringSync()) as YamlMap?; + + if (optionsYaml != null && optionsYaml.containsKey('uglify')) { + uglify = optionsYaml['uglify']; + } + } + + print('\u001b[33m [Fair Dart2JS] uglify option: => $uglify \u001b[0m'); + + var result = await dart2js.convertFile(partPath, uglify); File(jsName)..writeAsStringSync(result); } catch (e) { print('[Fair Dart2JS] e => ${e}'); diff --git a/compiler/pubspec.yaml b/compiler/pubspec.yaml index 3b806c05..952566c1 100644 --- a/compiler/pubspec.yaml +++ b/compiler/pubspec.yaml @@ -22,6 +22,7 @@ dependencies: glob: ^2.0.1 pedantic: ^1.11.1 http: ^0.13.3 + yaml: ^3.0.0 fair_annotation: ^2.3.0 # fair_annotation: @@ -31,9 +32,9 @@ dependencies: # fair_dart2dsl: # path: ../dart2dsl - fair_dart2js: ^1.4.0 -# fair_dart2js: -# path: ../dart2js +# fair_dart2js: ^1.4.0 + fair_dart2js: + path: ../dart2js #dependency_overrides: # collection: 1.17.0 From 744c0e53146edd98f2edbb17168160624a9e1133 Mon Sep 17 00:00:00 2001 From: lanhuajian Date: Tue, 20 Jun 2023 11:44:21 +0800 Subject: [PATCH 4/5] [feature] Makes Fair Runtime error reporting more explicit --- .../java/com/wuba/fair/channel/FairFfi.java | 15 +++ .../com/wuba/fair/core/FairV8JsLoader.java | 17 ++++ .../FairDynamicJSPlugin/FairDartBridge.m | 35 +++++++ fair/lib/src/internal/bind_data.dart | 19 +++- fair/lib/src/internal/error_tips.dart | 6 +- fair/lib/src/internal/stack_trace_detail.dart | 97 ++++++++++++------- .../src/internal/warning_dialog_widget.dart | 18 ++-- fair/lib/src/widget.dart | 50 +++++++++- 8 files changed, 209 insertions(+), 48 deletions(-) diff --git a/fair/android/src/main/java/com/wuba/fair/channel/FairFfi.java b/fair/android/src/main/java/com/wuba/fair/channel/FairFfi.java index 68d4d208..ded5a7d0 100644 --- a/fair/android/src/main/java/com/wuba/fair/channel/FairFfi.java +++ b/fair/android/src/main/java/com/wuba/fair/channel/FairFfi.java @@ -14,6 +14,9 @@ import androidx.annotation.Keep; +import org.json.JSONException; +import org.json.JSONObject; + /** * dart ffi */ @@ -66,6 +69,18 @@ public void runTask() { return result[0].toString(); } } + try { + //when an exception occurs, return the function name + JSONObject jsonObject = new JSONObject(args); + if (jsonObject.has("args")) { + JSONObject funObject = new JSONObject(jsonObject.optString("args")); + if(funObject.has("funcName")){ + return String.format("Runtime error while invoke JavaScript method:%s()", funObject.optString("funcName")); + } + } + } catch (JSONException e) { + e.printStackTrace(); + } return "jsAppObj is null"; } diff --git a/fair/android/src/main/java/com/wuba/fair/core/FairV8JsLoader.java b/fair/android/src/main/java/com/wuba/fair/core/FairV8JsLoader.java index b41b45d9..1c72a18e 100644 --- a/fair/android/src/main/java/com/wuba/fair/core/FairV8JsLoader.java +++ b/fair/android/src/main/java/com/wuba/fair/core/FairV8JsLoader.java @@ -11,6 +11,7 @@ import com.eclipsesource.v8.V8Array; import com.eclipsesource.v8.V8Function; import com.eclipsesource.v8.V8Object; +import com.eclipsesource.v8.V8ScriptCompilationException; import com.wuba.fair.FairPlugin; import com.wuba.fair.callback.JsResultCallback; import com.wuba.fair.constant.Constant; @@ -125,6 +126,22 @@ public void loadMainJs(Object arguments, JsResultCallback callback) { getV8JsExecutor().loadJS(jsName, jsLocalPath, callback); } catch (Exception e) { e.printStackTrace(); + //avoid loading all the time + if (callback != null) { + try { + JSONObject errorResult = new JSONObject(); + errorResult.put("status","error"); + if (e instanceof V8ScriptCompilationException){ + errorResult.put("errorInfo",e.toString()); + errorResult.put("lineNumber",((V8ScriptCompilationException) e).getLineNumber()); + }else{ + errorResult.put("errorInfo",e.getLocalizedMessage()); + } + callback.call(errorResult.toString()); + } catch (JSONException ex) { + + } + } } } diff --git a/fair/ios/Classes/FairDynamicJSPlugin/FairDartBridge.m b/fair/ios/Classes/FairDynamicJSPlugin/FairDartBridge.m index 233b3ef3..463fe9fe 100644 --- a/fair/ios/Classes/FairDynamicJSPlugin/FairDartBridge.m +++ b/fair/ios/Classes/FairDynamicJSPlugin/FairDartBridge.m @@ -69,6 +69,24 @@ - (void)setDartListener { if ([method isEqualToString:@"loadMainJs"]) { if ([strongSelf.delegate respondsToSelector:@selector(injectionJSScriptWtihJSScript: callback:)]) { [strongSelf.delegate injectionJSScriptWtihJSScript:model.path callback:^(id result, NSError *error) { + JSValue *value = result; + if (value && [value isKindOfClass:[JSValue class]]) { + NSString *str = value.toString; + if([str isEqualToString:@"undefined"]){ + NSMutableDictionary *result=[NSMutableDictionary dictionary]; + result[@"status"] = @"error"; + result[@"errorInfo"]=@"load JavaScript error"; + result[@"lineNumber"]=@-1; + + NSError *error = nil; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:result options:NSJSONWritingPrettyPrinted error:&error]; + NSString *jsonStr = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + + + callback(jsonStr); + return; + } + } callback(@"success"); }]; } @@ -163,6 +181,23 @@ - (const char *)executeScriptSyncImpl:(char *)args } NSString *result = [NSString stringWithFormat:@"%@", obj.toString]; FairLog(@"result:%@", result); + if([result isEqualToString:@"undefined"]){ + //取args中的funcName字段 + //arg ===> "{\"pageName\":\"null#0\",\"type\":\"method\",\"args\":{\"funcName\":\"_getAuth\",\"args\":null}}" + NSString *str = [NSString stringWithUTF8String:args]; + NSData *jsonData = [str dataUsingEncoding:NSUTF8StringEncoding]; + + NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil]; + + NSDictionary *args = dic[@"args"]; + NSString *funcName = args[@"funcName"]; + + FairLog(@"invoke funcName:%@",funcName); + + NSString *errorResult = [NSString stringWithFormat:@"Runtime error while invoke JavaScript method:%@()", funcName]; + + return errorResult.UTF8String; + } return result.UTF8String; } diff --git a/fair/lib/src/internal/bind_data.dart b/fair/lib/src/internal/bind_data.dart index 1247d413..cd925f15 100644 --- a/fair/lib/src/internal/bind_data.dart +++ b/fair/lib/src/internal/bind_data.dart @@ -68,8 +68,12 @@ class BindingData { } else { result = _functions?['runtimeInvokeMethodSync']?.call(funcName); } - var value = jsonDecode(result); - return value['result']['result']; + try { + var value = jsonDecode(result); + return value['result']['result']; + } catch (e) { + throw RuntimeError(errorMsg: result); + } } else { return _functions?[funcName]; } @@ -158,3 +162,14 @@ class BindingData { _functions?.clear(); } } + +class RuntimeError extends Error { + final String errorMsg; + + RuntimeError({required this.errorMsg}); + + @override + String toString() { + return errorMsg; + } +} diff --git a/fair/lib/src/internal/error_tips.dart b/fair/lib/src/internal/error_tips.dart index a5b6bce5..f29a66f4 100644 --- a/fair/lib/src/internal/error_tips.dart +++ b/fair/lib/src/internal/error_tips.dart @@ -6,7 +6,6 @@ import 'package:fair/src/internal/stack_trace_detail.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'warning_dialog_widget.dart'; @@ -18,6 +17,7 @@ class WarningWidget extends StatelessWidget { final BuildContext? parentContext; final String? stackTrace; final Map? errorBlock; + final List? highlightLines; const WarningWidget( @@ -28,7 +28,8 @@ class WarningWidget extends StatelessWidget { this.solution, this.parentContext, this.stackTrace, - this.errorBlock}) + this.errorBlock, + this.highlightLines}) : super(key: key); @override @@ -87,6 +88,7 @@ class WarningWidget extends StatelessWidget { name: name, errorJson: errorBlock, error: error, + highlightLines: highlightLines, ))); }, ); diff --git a/fair/lib/src/internal/stack_trace_detail.dart b/fair/lib/src/internal/stack_trace_detail.dart index 444b7e48..52b90c71 100644 --- a/fair/lib/src/internal/stack_trace_detail.dart +++ b/fair/lib/src/internal/stack_trace_detail.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:fair/src/extension.dart'; import 'package:flutter/material.dart'; class StackTraceDetailPage extends StatelessWidget { @@ -7,9 +8,10 @@ class StackTraceDetailPage extends StatelessWidget { final String? name; final Map? errorJson; final dynamic error; + final List? highlightLines; StackTraceDetailPage( - {required this.stackTrace, this.name, this.errorJson, this.error}); + {required this.stackTrace, this.name, this.errorJson, this.error,this.highlightLines}); @override Widget build(BuildContext context) { @@ -42,27 +44,7 @@ class StackTraceDetailPage extends StatelessWidget { color: Colors.black, ), children: [ - TextSpan( - text: '\nError Tag:', - style: TextStyle( - fontSize: 15.0, - color: Colors.black, - ), - ), - TextSpan( - text: name, - style: TextStyle( - fontWeight: FontWeight.bold, - background: Paint()..color = Colors.redAccent, - )), - TextSpan( - text: ', while parsing:\n', - style: TextStyle( - fontSize: 15.0, - color: Colors.black, - ), - ), - _buildFormattedJsonSpan(), + ..._getErrorTagNode(), TextSpan( text: '\n\nRuntime Error:\n', style: TextStyle( @@ -82,6 +64,34 @@ class StackTraceDetailPage extends StatelessWidget { ), ); + List _getErrorTagNode() { + if (errorJson == null) return []; + + return [ + TextSpan( + text: '\nError Tag:', + style: TextStyle( + fontSize: 15.0, + color: Colors.black, + ), + ), + TextSpan( + text: name, + style: TextStyle( + fontWeight: FontWeight.bold, + background: Paint()..color = Colors.redAccent, + )), + TextSpan( + text: ', while parsing:\n', + style: TextStyle( + fontSize: 15.0, + color: Colors.black, + ), + ), + _buildFormattedJsonSpan() + ]; + } + TextSpan _buildFormattedJsonSpan() { var encoder = JsonEncoder.withIndent(' '); final formattedJson = encoder.convert(errorJson); @@ -105,21 +115,38 @@ class StackTraceDetailPage extends StatelessWidget { final defaultTextStyle = TextStyle(color: Color(0xff333333), fontWeight: FontWeight.w500); - final spans = stackTraceLines.map((e) { - if (e.contains(PKG_PREFIX)) { - final span1 = e.substring(0, e.indexOf(PKG_PREFIX)); - final highlightSpan = - e.substring(e.indexOf(PKG_PREFIX), e.lastIndexOf(DART_SUFFIX) + 5); - final span2 = e.substring(e.lastIndexOf(DART_SUFFIX) + 5); - return TextSpan(text: span1, style: defaultTextStyle, children: [ - TextSpan( - text: highlightSpan, style: TextStyle(color: Color(0xFF3978C4))), - TextSpan(text: span2, style: defaultTextStyle), - TextSpan(text: '\n', style: defaultTextStyle), - ]); + final spans = stackTraceLines.mapEach((index, e) { + if (highlightLines?.isNotEmpty == true) { + //lineNumber => index+1 + if (highlightLines?.contains(index + 1) == true) { + //highlight + return TextSpan( + text: '$e\n', + style: TextStyle( + fontWeight: FontWeight.bold, + background: Paint()..color = Colors.redAccent, + )); + } else { + return TextSpan(text: '$e\n'); + } } else { - return TextSpan(text: '$e\n'); + if (e.contains(PKG_PREFIX)) { + final span1 = e.substring(0, e.indexOf(PKG_PREFIX)); + final highlightSpan = e.substring( + e.indexOf(PKG_PREFIX), e.lastIndexOf(DART_SUFFIX) + 5); + final span2 = e.substring(e.lastIndexOf(DART_SUFFIX) + 5); + + return TextSpan(text: span1, style: defaultTextStyle, children: [ + TextSpan( + text: highlightSpan, + style: TextStyle(color: Color(0xFF3978C4))), + TextSpan(text: span2, style: defaultTextStyle), + TextSpan(text: '\n', style: defaultTextStyle), + ]); + } else { + return TextSpan(text: '$e\n'); + } } }).toList(); diff --git a/fair/lib/src/internal/warning_dialog_widget.dart b/fair/lib/src/internal/warning_dialog_widget.dart index 47a61219..c3ea29a8 100644 --- a/fair/lib/src/internal/warning_dialog_widget.dart +++ b/fair/lib/src/internal/warning_dialog_widget.dart @@ -60,14 +60,16 @@ class DialogWidget extends Dialog { mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - 'Tag: $name', - style: TextStyle( - fontWeight: FontWeight.bold, - color: Color(0xffff0000), - fontSize: 20.0, - ), - ), + Visibility( + visible: name != null, + child: Text( + 'Tag: $name', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Color(0xffff0000), + fontSize: 20.0, + ), + )), SizedBox(height: 10), Text( 'Bundle: $url', diff --git a/fair/lib/src/widget.dart b/fair/lib/src/widget.dart index c3e8b427..2e43a5af 100644 --- a/fair/lib/src/widget.dart +++ b/fair/lib/src/widget.dart @@ -6,6 +6,7 @@ import 'dart:convert'; +import 'package:fair/src/internal/error_tips.dart'; import 'package:fair/src/runtime/fair_message_dispatcher.dart'; import 'package:fair/src/runtime/runtime_fair_delegate.dart'; import 'package:flutter/foundation.dart'; @@ -98,6 +99,11 @@ class FairState extends State with Loader, AutomaticKeepAliveClientM FairApp? _fairApp; String? bundleType; late String state2key; + bool isLoadJsError = false; + String? loadJsErrorInfo; + String? jsSource; + String? rawJsPath; + int? loadJsErrorLineNumber; // None nullable late FairDelegate delegate; @@ -128,7 +134,36 @@ class FairState extends State with Loader, AutomaticKeepAliveClientM } // if it's not in a tree, it's not unnecessary to load js any more. if(mounted) { - await Future.wait([_mFairApp.runtime.addScript(state2key, resolveJS, widget.data), _mFairApp.register(this)]); + final results = await Future.wait([ + _mFairApp.runtime.addScript(state2key, resolveJS, widget.data), + _mFairApp.register(this) + ]); + + //debug mode throw error message to WarningWidget + if (!kReleaseMode && results.isNotEmpty) { + print('addScript Result:${results.first}'); + try { + final addScriptResult = results.first.toString(); + var errorResult = jsonDecode(addScriptResult); + isLoadJsError = (errorResult['status'] == 'error'); + + if (isLoadJsError) { + loadJsErrorInfo = errorResult['errorInfo'] ?? ''; + loadJsErrorLineNumber = errorResult['lineNumber']; + + //debug mode use json + if(widget.path?.endsWith('.fair.json') == true){ + rawJsPath = widget.path?.replaceFirst('.fair.json', '.fair.js') ?? + widget.path; + + jsSource = await rootBundle.loadString(rawJsPath!); + } + } + } catch (e) { + print(e); + } + } + if(mounted) { delegate.didChangeDependencies(); _reload(); @@ -164,6 +199,19 @@ class FairState extends State with Loader, AutomaticKeepAliveClientM Widget build(BuildContext context) { super.build(context); assert(_fairApp != null, 'FairWidget must be descendant of FairApp'); + if (!kReleaseMode && isLoadJsError) { + return Scaffold( + body: WarningWidget( + parentContext: context, + url: rawJsPath, + error: loadJsErrorInfo, + stackTrace: jsSource, + highlightLines: [loadJsErrorLineNumber ?? -1], + solution: + 'Unsupported JavaScript syntax, please check and correct your Dart method coding!', + ), + ); + } var builder = widget.holder ?? _fairApp?.placeholderBuilder; var result = _child ?? builder?.call(context); if (!kReleaseMode && _fairApp!.debugShowFairBanner) { From 82c40706b62b613702f426632bc1d1f9156471ef Mon Sep 17 00:00:00 2001 From: lanhuajian Date: Tue, 20 Jun 2023 14:44:17 +0800 Subject: [PATCH 5/5] [feature] Additional --- compiler/pubspec.yaml | 6 +++--- .../src/main/java/com/wuba/fair/core/FairV8JsLoader.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/pubspec.yaml b/compiler/pubspec.yaml index 952566c1..2ee5c880 100644 --- a/compiler/pubspec.yaml +++ b/compiler/pubspec.yaml @@ -32,9 +32,9 @@ dependencies: # fair_dart2dsl: # path: ../dart2dsl -# fair_dart2js: ^1.4.0 - fair_dart2js: - path: ../dart2js + fair_dart2js: ^1.4.0 +# fair_dart2js: +# path: ../dart2js #dependency_overrides: # collection: 1.17.0 diff --git a/fair/android/src/main/java/com/wuba/fair/core/FairV8JsLoader.java b/fair/android/src/main/java/com/wuba/fair/core/FairV8JsLoader.java index 1c72a18e..86c02c04 100644 --- a/fair/android/src/main/java/com/wuba/fair/core/FairV8JsLoader.java +++ b/fair/android/src/main/java/com/wuba/fair/core/FairV8JsLoader.java @@ -139,7 +139,7 @@ public void loadMainJs(Object arguments, JsResultCallback callback) { } callback.call(errorResult.toString()); } catch (JSONException ex) { - + callback.call("result"); } } }