diff --git a/dart2js/README.md b/dart2js/README.md index 4910bb7d..8ccb1ea7 100644 --- a/dart2js/README.md +++ b/dart2js/README.md @@ -1,3 +1,12 @@ # fair_dart2js -fair_compiler 的配套编译器,用于将 Dart 转为 JS。 \ No newline at end of file +fair_compiler 的配套编译器,用于将 Dart 转为 JS。 + +### 开发功能记录 +* try、catch + * 不支持 on 函数 例如 on Exception catch (e,s),此类写法将会抛弃处理,请直接写 catch (e) + +### 待开发 +#### 原生函数转换 +* print +* Exception \ No newline at end of file diff --git a/dart2js/lib/index.dart b/dart2js/lib/index.dart index 45ebc42b..3c20e795 100644 --- a/dart2js/lib/index.dart +++ b/dart2js/lib/index.dart @@ -4,7 +4,9 @@ * found in the LICENSE file. */ import 'dart:async'; -import './src/convertor.dart' show convertWidgetStateFile; + +import 'src/convertorV2.dart'; + Future convertFile(String input, [bool compress = false]) async { return convertWidgetStateFile(input, compress); diff --git a/dart2js/lib/src/__test_data__/tryCatch/tryTest.dart b/dart2js/lib/src/__test_data__/tryCatch/tryTest.dart new file mode 100644 index 00000000..23d0bc41 --- /dev/null +++ b/dart2js/lib/src/__test_data__/tryCatch/tryTest.dart @@ -0,0 +1,22 @@ +import 'package:fair_dart2js/src/covert/convertFunction.dart'; +/** + * 正常测试 try catch + * */ + +String a = convertFunction(''' +void testTry() { + try { + int a=1; + int b=2; + int c=a+b; + print("1"); + throw FormatException('Expected at least 1 section'); + } catch (e,s) { + print('111'+e); + } +} + '''); + +void main() { + print(a); +} diff --git a/dart2js/lib/src/__test_data__/tryCatch/tryTest2.dart b/dart2js/lib/src/__test_data__/tryCatch/tryTest2.dart new file mode 100644 index 00000000..90afdab8 --- /dev/null +++ b/dart2js/lib/src/__test_data__/tryCatch/tryTest2.dart @@ -0,0 +1,25 @@ +import 'package:fair_dart2js/src/covert/convertFunction.dart'; + +/** + * 测试 on 函数将被丢弃 + * */ + +String a = convertFunction(''' +void testTry() { + try { + int a=1; + int b=2; + int c=a+b; + print("1"); + throw FormatException('Expected at least 1 section'); + } on Exception catch (e,s) { + print('111'+e); + } catch (e) { + print('222'+e); + } +} + '''); + +void main() { + print(a); +} diff --git a/dart2js/lib/src/__test_data__/tryCatch/tryTest3.dart b/dart2js/lib/src/__test_data__/tryCatch/tryTest3.dart new file mode 100644 index 00000000..568cdcab --- /dev/null +++ b/dart2js/lib/src/__test_data__/tryCatch/tryTest3.dart @@ -0,0 +1,21 @@ +import 'package:fair_dart2js/src/covert/convertFunction.dart'; + +/** + * 测试 finally 处理 + * */ + +String a = convertFunction(''' +void testTry() { + try { + int a=1; + } catch (e,s) { + print('catch:'+e); + } finally{ + print('finally...'); + } +} + '''); + +void main() { + print(a); +} diff --git a/dart2js/lib/src/convertor.dart b/dart2js/lib/src/convertor.dart index 189c791c..dd2ed4b0 100644 --- a/dart2js/lib/src/convertor.dart +++ b/dart2js/lib/src/convertor.dart @@ -1754,3 +1754,5 @@ String uglify(String str) { } return buf.toString(); } + + diff --git a/dart2js/lib/src/convertorV2.dart b/dart2js/lib/src/convertorV2.dart new file mode 100644 index 00000000..a8a86bfb --- /dev/null +++ b/dart2js/lib/src/convertorV2.dart @@ -0,0 +1,14 @@ +export 'covert/convertClassString.dart'; +export 'covert/convertExpression.dart'; +export 'covert/convertFunction.dart'; +export 'covert/convertArrayFuncExpression.dart'; +export 'covert/convertBlock.dart'; +export 'covert/convertFunctionExpression.dart'; +export 'covert/convertStatements.dart'; +export 'covert/convertWidgetStateFile.dart'; +export 'covert/convertFunctionFromData.dart'; +export 'declaration/ClassDeclarationData.dart'; +export 'declaration/FieldDeclarationData.dart'; +export 'declaration/MethodDeclarationData.dart'; + + diff --git a/dart2js/lib/src/convertor_test.dart b/dart2js/lib/src/convertor_test.dart index e7313548..27d16ddb 100644 --- a/dart2js/lib/src/convertor_test.dart +++ b/dart2js/lib/src/convertor_test.dart @@ -3,8 +3,9 @@ * Use of this source code is governed by a BSD type license that can be * found in the LICENSE file. */ -import 'convertor.dart'; +import 'convertorV2.dart'; import 'dart:io' show Platform; +import 'package:fair_dart2js/src/visitor/UniqueNameGenerator.dart'; import 'package:path/path.dart' show dirname; bool compare(String testcaseName, String actual, String expected) { @@ -13,7 +14,7 @@ bool compare(String testcaseName, String actual, String expected) { } var isNotEqual = (actual.isEmpty && expected.isNotEmpty) || - (actual.isNotEmpty && expected.isEmpty); + (actual.isNotEmpty && expected.isEmpty); if (isNotEqual) { print('''$testcaseName Actual: $actual \r\nExpected: $expected'''); return false; @@ -31,7 +32,8 @@ bool compare(String testcaseName, String actual, String expected) { continue; } if (actual[i] != expected[j]) { - print('''$testcaseName Actual: $actual \r\nExpected: $expected \r\nat position $i(${actual.substring(i, i+30)}) $j(${expected.substring(j, j+30)})'''); + print( + '''$testcaseName Actual: $actual \r\nExpected: $expected \r\nat position $i(${actual.substring(i, i + 30)}) $j(${expected.substring(j, j + 30)})'''); return false; } else { i++; @@ -43,7 +45,8 @@ bool compare(String testcaseName, String actual, String expected) { i++; continue; } else { - print('''$testcaseName Actual: $actual \r\nExpected: $expected \r\nat position $i(${actual[i]}) $j()'''); + print( + '''$testcaseName Actual: $actual \r\nExpected: $expected \r\nat position $i(${actual[i]}) $j()'''); return false; } } @@ -52,7 +55,8 @@ bool compare(String testcaseName, String actual, String expected) { j++; continue; } else { - print('''$testcaseName Actual: $actual \r\nExpected: $expected \r\nat position $i() $j(${expected[j]})'''); + print( + '''$testcaseName Actual: $actual \r\nExpected: $expected \r\nat position $i() $j(${expected[j]})'''); return false; } } @@ -1002,12 +1006,20 @@ bool testcaseDeclareList() { } bool testcaseSuper() { - return compare('testcaseSuper', convertFunctionFromData(MethodDeclarationData('testcaseSuper', ''' + return compare( + 'testcaseSuper', + convertFunctionFromData( + MethodDeclarationData( + 'testcaseSuper', + ''' void testcaseSuper() { super.initState(); futureBuilderFuture = _generateData(this); } - ''', false), ClassDeclarationData()..parentClass='Parent'), ''' + ''', + false), + ClassDeclarationData()..parentClass = 'Parent'), + ''' function testcaseSuper() { const __thiz__ = this; @@ -1418,7 +1430,8 @@ res.total = json.total; bool testcaseClassDeclareFactoryConstructor() { UniqueNameGenerator().reset(); - return compare('testcaseClassDeclareFactoryConstructor', convertClassString(r''' + return compare( + 'testcaseClassDeclareFactoryConstructor', convertClassString(r''' class E { int e; @@ -1472,7 +1485,8 @@ E.ee1 = (str) => E.ee(int.parse(str)); bool testcaseClassDeclareDelegateConstructor() { UniqueNameGenerator().reset(); - return compare('testcaseClassDeclareDelegateConstructor', convertClassString(r''' + return compare( + 'testcaseClassDeclareDelegateConstructor', convertClassString(r''' class E { int e; @@ -1666,7 +1680,7 @@ b = 1; // print(super.a.b); // } // '''), ''' - + // '''); // } @@ -2474,7 +2488,11 @@ this.g3 = xx; } bool testcaseImport() { - return compare('testcaseImport', convertWidgetStateFile(dirname(Platform.script.path) + '/__test_data__/page1/page1.dart'), ''' + return compare( + 'testcaseImport', + convertWidgetStateFile( + dirname(Platform.script.path) + '/__test_data__/page1/page1.dart'), + ''' GLOBAL['#FairKey#'] = (function(__initProps__) { const __global__ = this; defineModule(5, function(__mod__) { @@ -2765,7 +2783,6 @@ fairProps = widget._props; } void main() { - print('Executing testcases...'); var arr = [ testcaseFor1, @@ -2844,4 +2861,4 @@ void main() { } else { print('''Testcase total ${arr.length} successful'''); } -} \ No newline at end of file +} diff --git a/dart2js/lib/src/covert/convertArrayFuncExpression.dart b/dart2js/lib/src/covert/convertArrayFuncExpression.dart new file mode 100644 index 00000000..03545988 --- /dev/null +++ b/dart2js/lib/src/covert/convertArrayFuncExpression.dart @@ -0,0 +1,21 @@ +import 'package:analyzer/dart/ast/ast.dart'; + +import '../node/ArrowFunctionExpressionNode.dart'; +import '../node/GenericStatementNode.dart'; +import 'convertExpression.dart'; + +String convertArrayFuncExpression(FunctionExpression code) { + var body = code.body as ExpressionFunctionBody; + if (body.functionDefinition.toString() == '=>') { + var gnNode = ArrowFunctionExpressionNode(); + // TODO: 支持命名参数、可选参数 + code.parameters?.parameters.forEach((element) { + gnNode.argumentList.add([element.identifier.toString()]); + }); + gnNode.body.push( + GenericStatementNode(convertExpression(body.expression.toString()))); + return gnNode.toSource(); + } else { + throw 'error'; + } +} \ No newline at end of file diff --git a/dart2js/lib/src/covert/convertBlock.dart b/dart2js/lib/src/covert/convertBlock.dart new file mode 100644 index 00000000..02a1ccf3 --- /dev/null +++ b/dart2js/lib/src/covert/convertBlock.dart @@ -0,0 +1,22 @@ +import 'package:analyzer/dart/analysis/utilities.dart'; + +import '../visitor/SimpleFunctionGenerator.dart'; +import '../funs/shouldErrorBeIgnored.dart'; + +String convertBlock(String code) { + // print("[convertBlock]" + code); + var res = parseString( + content: '''dummy() async $code''', throwIfDiagnostics: false); + + if (res.errors.isNotEmpty) { + if (shouldErrorBeIgnored(res.errors)) { + // ignore + } else { + throw ArgumentError(); + } + } + + var generator = SimpleFunctionGenerator(); + res.unit.visitChildren(generator); + return generator.func?.body.toSource() ?? ''; +} diff --git a/dart2js/lib/src/covert/convertClassString.dart b/dart2js/lib/src/covert/convertClassString.dart new file mode 100644 index 00000000..cf07967b --- /dev/null +++ b/dart2js/lib/src/covert/convertClassString.dart @@ -0,0 +1,12 @@ +import 'package:analyzer/dart/analysis/features.dart'; +import 'package:analyzer/dart/analysis/utilities.dart'; + +import '../visitor/ClassDeclarationVisitor.dart'; + +String convertClassString(String content, [bool isDataBean = false]) { + var result = + parseString(content: content, featureSet: FeatureSet.fromEnableFlags([])); + var visitor = ClassDeclarationVisitor(isDataBean); + result.unit.visitChildren(visitor); + return visitor.genJsCode(); +} \ No newline at end of file diff --git a/dart2js/lib/src/covert/convertExpression.dart b/dart2js/lib/src/covert/convertExpression.dart new file mode 100644 index 00000000..4af1fd56 --- /dev/null +++ b/dart2js/lib/src/covert/convertExpression.dart @@ -0,0 +1,22 @@ +import 'convertStatements.dart'; + +String convertExpression(String code) { + // print("[convertExpression]" + code); + var res = ''; + var start = 0; + try { + res = convertStatements(code + ';'); + res = res.trim(); + } on ArgumentError { + // 有些表达式直接变成语句会报错,例如字典字面量对象 + var prefix = 'var __variable__ = '; + res = convertStatements('''$prefix$code;'''); + res = res.trim(); + start = res.indexOf('=') + 1; + } + var end = res.length - 1; + while (end >= 0 && RegExp(r'[\s\r\n;]', multiLine: false).hasMatch(res[end])) { + end--; + } + return res.substring(start, end + 1); +} \ No newline at end of file diff --git a/dart2js/lib/src/covert/convertFunction.dart b/dart2js/lib/src/covert/convertFunction.dart new file mode 100644 index 00000000..8bb0a217 --- /dev/null +++ b/dart2js/lib/src/covert/convertFunction.dart @@ -0,0 +1,16 @@ +import 'package:analyzer/dart/analysis/utilities.dart'; + +import '../visitor/SimpleFunctionGenerator.dart'; + +String convertFunction(String code, + {bool isArrow = false, + bool isClassMethod = false, + bool classHasStaticFields = false}) { + var res = parseString(content: code); + var generator = SimpleFunctionGenerator(isArrowFunc: isArrow); + res.unit.visitChildren(generator); + generator.func + ?..withContext = isClassMethod + ..classHasStaticFields = classHasStaticFields; + return generator.func?.toSource() ?? ''; +} \ No newline at end of file diff --git a/dart2js/lib/src/covert/convertFunctionExpression.dart b/dart2js/lib/src/covert/convertFunctionExpression.dart new file mode 100644 index 00000000..fb71b2d7 --- /dev/null +++ b/dart2js/lib/src/covert/convertFunctionExpression.dart @@ -0,0 +1,6 @@ +import 'convertFunction.dart'; + +String convertFunctionExpression(String code) { + code = 'dummy' + code; + return convertFunction(code); +} \ No newline at end of file diff --git a/dart2js/lib/src/covert/convertFunctionFromData.dart b/dart2js/lib/src/covert/convertFunctionFromData.dart new file mode 100644 index 00000000..50ec3665 --- /dev/null +++ b/dart2js/lib/src/covert/convertFunctionFromData.dart @@ -0,0 +1,42 @@ +import 'package:analyzer/dart/analysis/utilities.dart'; + +import '../visitor/SimpleFunctionGenerator.dart'; +import '../declaration/ClassDeclarationData.dart'; +import '../node/GenericStatementNode.dart'; +import '../declaration/MethodDeclarationData.dart'; + +String convertFunctionFromData(MethodDeclarationData? data, + [ClassDeclarationData? ctx]) { + var content = data?.body ?? ''; + if (data?.isStatic ?? false) content = content.replaceAll('static', ''); + var res = parseString(content: content); + var generator = SimpleFunctionGenerator( + isArrowFunc: data?.isArrow ?? false, + renamedParameters: data?.renamedParameters, + parentClass: ctx?.parentClass); + generator.func + ?..withContext = true + ..classHasStaticFields = + (ctx?.fields.any((element) => element.isStatic) ?? false) || + (ctx?.methods.any((element) => + element.isStatic && + !element.isFactory && + !element.isGenerativeConstructor) ?? + false) + ..isStatic = data?.isStatic ?? false; + res.unit.visitChildren(generator); + + if (ctx != null) { + generator.func?.className = ctx.className; + generator.func + ?..isGenerativeConstructor = data?.isGenerativeConstructor ?? false + ..isRedirectConstructor = data?.isRedirectConstructor ?? false; + } + + if (data?.abtractedInitializer != null && + data!.abtractedInitializer.isNotEmpty) { + generator.func?.body.statements.insert( + 0, GenericStatementNode(data.abtractedInitializer.join('\r\n'))); + } + return generator.func?.toSource() ?? ''; +} \ No newline at end of file diff --git a/dart2js/lib/src/covert/convertStatements.dart b/dart2js/lib/src/covert/convertStatements.dart new file mode 100644 index 00000000..bb91ebeb --- /dev/null +++ b/dart2js/lib/src/covert/convertStatements.dart @@ -0,0 +1,9 @@ +import 'convertBlock.dart'; + +String convertStatements(String code) { + if (!code.endsWith(';') && !code.endsWith('}')) { + code += ';'; + } + var res = convertBlock('''{$code}'''); + return res; +} \ No newline at end of file diff --git a/dart2js/lib/src/covert/convertWidgetStateFile.dart b/dart2js/lib/src/covert/convertWidgetStateFile.dart new file mode 100644 index 00000000..5926d209 --- /dev/null +++ b/dart2js/lib/src/covert/convertWidgetStateFile.dart @@ -0,0 +1,57 @@ +// +// 去掉类型转换 +// 支持匿名集合对象 +// try-catch +// yeild +// 列表、Map对象、RegExp、Date、其他基础数据类型及其对应操作 +// +// 操作符:,、~/ +// escaped string(r修饰符) +// int.parse +// operator override +// get & set +// 高级特性:扩展方法 & part文件合并 & mixin & noSuchMethod +// 范型 +// 注解(annotation) +// async: stream completer yield zone +// 省略的this +// +// +// done +// declare +// asignment +// static +// extend & super & constructor +// if / for / do-while / while-do / switch / for-in +// continue&break +// method-invoke&cacades +// inner function +// async&await +// new操作 +// ?. / ... / ?: if表达式 +// 对 bool表达式中的 is 进行处理 +// 表达式嵌套 +// is 类型检测 直接判断对象是否为空 +// as 类型转换 直接去掉类型转换 +// +import 'dart:io'; + +import 'package:analyzer/dart/analysis/features.dart'; +import 'package:analyzer/dart/analysis/utilities.dart'; + +import '../utils/const.dart'; +import '../utils/uglify.dart'; +import '../visitor/WidgetStateGenerator.dart'; + +String convertWidgetStateFile(String filePath, [bool isCompressed = false]) { + var file = File(filePath); + var stateFilePath = + Platform.isWindows ? filePath : file.absolute.uri.normalizePath().path; + var result = parseFile( + path: stateFilePath, featureSet: FeatureSet.fromEnableFlags([])); + var visitor = WidgetStateGenerator(stateFilePath); + result.unit.visitChildren(visitor); + + transpileOption.modifySetState = true; + return isCompressed ? uglify(visitor.genJsCode()) : visitor.genJsCode(); +} \ No newline at end of file diff --git a/dart2js/lib/src/declaration/ClassDeclarationData.dart b/dart2js/lib/src/declaration/ClassDeclarationData.dart new file mode 100644 index 00000000..9d2f4217 --- /dev/null +++ b/dart2js/lib/src/declaration/ClassDeclarationData.dart @@ -0,0 +1,150 @@ +import 'FieldDeclarationData.dart'; +import 'MethodDeclarationData.dart'; +import '../covert/convertFunctionFromData.dart'; +import 'package:fair_dart2js/src/list_ext.dart'; +import '../utils/const.dart'; + + + +class ClassDeclarationData { + String? className = ''; + var fields = []; + var methods = []; + var outputTemplateType = ClassOutputTemplateType.raw; + String? parentClass = ''; + bool isDataBean = false; + + String genJsCode() { + var tpl = ''; + switch (outputTemplateType) { + case ClassOutputTemplateType.raw: + { + var fieldsLiterval = ''; + fields.where((element) => !element.isStatic).forEach((element) { + element.isGetter + ? fieldsLiterval += + 'this.${element.name} = (function(_this) { with (_this) {${element + .initVal ?? 'null'} } })(this);' + : fieldsLiterval += + 'this.${element.name} = ${element.initVal};'; + }); + var memberMethodsLiterval = ''; + methods.where((element) => !(element.isStatic)).forEach((element) { + memberMethodsLiterval += + '${element.name}: ${convertFunctionFromData(element, this)},'; + }); + var staticFieldsLiteral = fields + .where((element) => element.isStatic) + .map((e) => + e.isGetter + ? '$className.${e.name} = (function() { with ($className) {${e + .initVal ?? 'null'}} })();' + : '$className.${e + .name} = (function() { with ($className) { return ${e.initVal ?? + 'null'}; } })();') + .join('\r\n'); + var staticMethodsLiteral = methods + .where((element) => element.isStatic) + .map((e) => + '$className.${e.name} = ${convertFunctionFromData(e, this)};') + .join('\r\n'); + var defaultContructorIsFactory = methods.firstWhereOrNull( + (element) => + element.isFactory == true && + element.name == factoryConstructorAlias, + orElse: () => null) != + null; + var autoGenDefaultConstructor = methods.firstWhereOrNull( + (element) => + !element.isStatic && element.name == constructorAlias, + orElse: () => null) == + null; + var defaultConstructor = ''' + $className.prototype.$constructorAlias = function() { + ${(parentClass != null && parentClass!.isNotEmpty) + ? parentClass + : 'Object'}.prototype.$constructorAlias.call(this); + }; + '''; + var instanceConstruction = !defaultContructorIsFactory + ? ''' + const inner = $className.$innerName; + if (this == __global__) { + return new $className({$argsName: arguments}); + } else { + const args = arguments.length > 0 ? arguments[0].$argsName || arguments : []; + inner.apply(this, args); + $className.prototype.$constructorAlias.apply(this, args); + return this; + } + ''' + : ''' + return $className.$factoryConstructorAlias.apply($className, arguments); + '''; + var beanToJson = ''' + toJson: function() { + let res = {}; + ${fields.map((element) => 'res.${element.name} = this.${element + .name};').join('\r\n')} + return JSON.stringify(res); + }, + '''; + var beanFromJson = ''' + $className.fromJson = function(json) { + if (typeof json == 'string') { + json = JSON.parse(json); + } + var res = new $className(); + ${fields.map((element) => 'res.${element.name} = json.${element + .name};').join('\r\n')} + return res; + }; + '''; + tpl = ''' + function $className() { + $instanceConstruction + } + $className.$innerName = function inner() { + ${parentClass != null && parentClass!.isNotEmpty + ? '$parentClass.$innerName.call(this);' + : ''} + $fieldsLiterval + }; + $className.prototype = { + $memberMethodsLiterval + ${isDataBean ? beanToJson : ''} + }; + ${autoGenDefaultConstructor ? defaultConstructor : ''} + $staticMethodsLiteral + $staticFieldsLiteral + ${isDataBean ? beanFromJson : ''} + ${parentClass != null && parentClass!.isNotEmpty + ? 'inherit($className, $parentClass);' + : ''} + '''; + } + break; + case ClassOutputTemplateType.pageState: + { + var fieldsLiterval = ''; + fields.forEach((element) { + fieldsLiterval += '${element.name}: ${element.initVal},'; + }); + var methodsLiterval = ''; + methods.forEach((element) { + methodsLiterval += + '${element.name}: ${convertFunctionFromData(element, this)},'; + }); + tpl = ''' + { + $fieldsLiterval + $methodsLiterval + } + '''; + } + + break; + } + return tpl; + } +} diff --git a/dart2js/lib/src/declaration/FieldDeclarationData.dart b/dart2js/lib/src/declaration/FieldDeclarationData.dart new file mode 100644 index 00000000..f7e5e294 --- /dev/null +++ b/dart2js/lib/src/declaration/FieldDeclarationData.dart @@ -0,0 +1,8 @@ +class FieldDeclarationData { + String? name; + String? initVal; + var isStatic = false; + var isGetter = false; + + FieldDeclarationData(this.name, this.initVal); +} \ No newline at end of file diff --git a/dart2js/lib/src/declaration/MethodDeclarationData.dart b/dart2js/lib/src/declaration/MethodDeclarationData.dart new file mode 100644 index 00000000..63bf8ef3 --- /dev/null +++ b/dart2js/lib/src/declaration/MethodDeclarationData.dart @@ -0,0 +1,55 @@ +import 'dart:collection'; + +class MethodDeclarationData { + var name = ''; + var body = ''; + var _isGenerativeConstructor = false; + var _isRedirectConstructor = false; + var isArrow = false; + var _isStatic = false; + var _isFactory = false; + + bool get isRedirectConstructor => _isRedirectConstructor; + + set isRedirectConstructor(bool val) { + _isRedirectConstructor = true; + if (val) { + _isGenerativeConstructor = val; + } + } + + bool get isStatic => _isStatic; + + set isStatic(bool val) { + if (!val && (_isFactory || _isGenerativeConstructor)) { + throw 'invalid value'; + } + _isStatic = val; + } + + bool get isGenerativeConstructor => _isGenerativeConstructor; + + set isGenerativeConstructor(bool val) { + if (isRedirectConstructor && !val) { + throw 'invalid value'; + } + _isGenerativeConstructor = val; + if (val) { + isStatic = val; + } + } + + bool get isFactory => _isFactory; + + set isFactory(bool val) { + _isFactory = val; + if (val) { + isStatic = val; + } + } + + var renamedParameters = HashMap(); + var abtractedInitializer = []; + + MethodDeclarationData(this.name, this.body, this.isArrow); +} \ No newline at end of file diff --git a/dart2js/lib/src/funs/addCommaAsNeeded.dart b/dart2js/lib/src/funs/addCommaAsNeeded.dart new file mode 100644 index 00000000..68e583a9 --- /dev/null +++ b/dart2js/lib/src/funs/addCommaAsNeeded.dart @@ -0,0 +1,6 @@ +String? addCommaAsNeeded(String? statement) { + if (statement?.endsWith(';') == true) { + return statement; + } + return (statement ?? '') + ';'; +} \ No newline at end of file diff --git a/dart2js/lib/src/funs/removeCommaAsNeeded.dart b/dart2js/lib/src/funs/removeCommaAsNeeded.dart new file mode 100644 index 00000000..b1d33633 --- /dev/null +++ b/dart2js/lib/src/funs/removeCommaAsNeeded.dart @@ -0,0 +1,6 @@ +String removeCommaAsNeeded(String statement) { + if (!statement.endsWith(';')) { + return statement; + } + return statement.substring(0, statement.lastIndexOf(';')); +} \ No newline at end of file diff --git a/dart2js/lib/src/funs/shouldErrorBeIgnored.dart b/dart2js/lib/src/funs/shouldErrorBeIgnored.dart new file mode 100644 index 00000000..edcddc6e --- /dev/null +++ b/dart2js/lib/src/funs/shouldErrorBeIgnored.dart @@ -0,0 +1,7 @@ +import 'package:analyzer/error/error.dart'; +import 'package:fair_dart2js/src/list_ext.dart'; + +bool shouldErrorBeIgnored(List errors) { + var ignoredErrors = ['CONTINUE_OUTSIDE_OF_LOOP', 'BREAK_OUTSIDE_OF_LOOP']; + return errors.firstWhereOrNull((err) => !ignoredErrors.contains(err.errorCode.name), orElse: () => null) == null; +} \ No newline at end of file diff --git a/dart2js/lib/src/node/ArrowFunctionExpressionNode.dart b/dart2js/lib/src/node/ArrowFunctionExpressionNode.dart new file mode 100644 index 00000000..65f47435 --- /dev/null +++ b/dart2js/lib/src/node/ArrowFunctionExpressionNode.dart @@ -0,0 +1,11 @@ +import 'FunctionDeclarationNode.dart'; +import '../funs/addCommaAsNeeded.dart'; + +class ArrowFunctionExpressionNode extends FunctionDeclarationNode { + @override + String toSource() { + return ''' + ${isAsync ? 'async ' : ''}(${argumentList.map((elem) => elem[0]).join(',')}) => ${addCommaAsNeeded(body.statements[0].toSource())} + '''; + } +} \ No newline at end of file diff --git a/dart2js/lib/src/node/AssignmentStatementNode.dart b/dart2js/lib/src/node/AssignmentStatementNode.dart new file mode 100644 index 00000000..a8269faa --- /dev/null +++ b/dart2js/lib/src/node/AssignmentStatementNode.dart @@ -0,0 +1,14 @@ +import 'GenericStatementNode.dart'; +import 'StatementNode.dart'; +import '../funs/addCommaAsNeeded.dart'; + +class AssignmentStatementNode extends StatementNode { + String? leftSide; + String operator_ = '='; + GenericStatementNode? rightSide; + + @override + String toSource() { + return '$leftSide $operator_ ${addCommaAsNeeded(rightSide?.toSource())}'; + } +} \ No newline at end of file diff --git a/dart2js/lib/src/node/AwaitStatementNode.dart b/dart2js/lib/src/node/AwaitStatementNode.dart new file mode 100644 index 00000000..e74bf85b --- /dev/null +++ b/dart2js/lib/src/node/AwaitStatementNode.dart @@ -0,0 +1,11 @@ +import 'StatementNode.dart'; +import '../funs/addCommaAsNeeded.dart'; + +class AwaitStatementNode extends StatementNode { + StatementNode? expr; + + @override + String toSource() { + return '''await ${addCommaAsNeeded(expr?.toSource())}'''; + } +} \ No newline at end of file diff --git a/dart2js/lib/src/node/BinaryExpressionNode.dart b/dart2js/lib/src/node/BinaryExpressionNode.dart new file mode 100644 index 00000000..97e55e03 --- /dev/null +++ b/dart2js/lib/src/node/BinaryExpressionNode.dart @@ -0,0 +1,13 @@ +import 'GenericStatementNode.dart'; +import 'StatementNode.dart'; + +class BinaryExpressionNode extends StatementNode { + String? operator; + GenericStatementNode? left; + GenericStatementNode? right; + + @override + String toSource() { + return '''${left?.toSource()}$operator${right?.toSource()}'''; + } +} \ No newline at end of file diff --git a/dart2js/lib/src/node/CascadeOperatorStatementNode.dart b/dart2js/lib/src/node/CascadeOperatorStatementNode.dart new file mode 100644 index 00000000..b2b48a26 --- /dev/null +++ b/dart2js/lib/src/node/CascadeOperatorStatementNode.dart @@ -0,0 +1,21 @@ +import 'MemberAccessStatementNode.dart'; +import 'StatementNode.dart'; + +class CascadeOperatorStatementNode extends StatementNode { + String? target; + List? cascades; + + @override + String toSource() { + var tempThiz = '__target__'; + cascades?.forEach((element) { + element.thiz = tempThiz; + }); + return '''(function() { + let $tempThiz = $target; + ${cascades?.map((e) => e.toSource()).join('')} + return $tempThiz; + })() + '''; + } +} \ No newline at end of file diff --git a/dart2js/lib/src/node/CatchStatementNode.dart b/dart2js/lib/src/node/CatchStatementNode.dart new file mode 100644 index 00000000..e7de0e42 --- /dev/null +++ b/dart2js/lib/src/node/CatchStatementNode.dart @@ -0,0 +1,6 @@ +import 'StatementNode.dart'; + +class CatchStatementNode extends StatementNode { + String? body; + +} diff --git a/dart2js/lib/src/node/ConditionalExpressionNode.dart b/dart2js/lib/src/node/ConditionalExpressionNode.dart new file mode 100644 index 00000000..71d01e38 --- /dev/null +++ b/dart2js/lib/src/node/ConditionalExpressionNode.dart @@ -0,0 +1,14 @@ +// 不支持如下运算符的嵌套:三目运算符、类型测试 +import 'GenericStatementNode.dart'; +import 'StatementNode.dart'; + +class ConditionalExpressionNode extends StatementNode { + GenericStatementNode? condition; + GenericStatementNode? then; + GenericStatementNode? elseExpr; + + @override + String toSource() { + return '''${condition?.toSource()} ? ${then?.toSource()} : ${elseExpr?.toSource()}'''; + } +} diff --git a/dart2js/lib/src/node/DeclarationStatmentNode.dart b/dart2js/lib/src/node/DeclarationStatmentNode.dart new file mode 100644 index 00000000..d24f8fb2 --- /dev/null +++ b/dart2js/lib/src/node/DeclarationStatmentNode.dart @@ -0,0 +1,12 @@ +import 'StatementNode.dart'; + +class DeclarationStatmentNode extends StatementNode { + List variables = []; + + @override + String toSource() { + return ''' + let ${variables.join(',')}; + '''; + } +} \ No newline at end of file diff --git a/dart2js/lib/src/node/DoWhileStatementNode.dart b/dart2js/lib/src/node/DoWhileStatementNode.dart new file mode 100644 index 00000000..431be9dc --- /dev/null +++ b/dart2js/lib/src/node/DoWhileStatementNode.dart @@ -0,0 +1,12 @@ +import 'WhileStatementNode.dart'; + +class DoWhileStatementNode extends WhileStatementNode { + @override + String toSource() { + return ''' + do { + $body + } while ($condition); + '''; + } +} \ No newline at end of file diff --git a/dart2js/lib/src/node/ForInStatementNode.dart b/dart2js/lib/src/node/ForInStatementNode.dart new file mode 100644 index 00000000..7419480f --- /dev/null +++ b/dart2js/lib/src/node/ForInStatementNode.dart @@ -0,0 +1,17 @@ +import 'GenericStatementNode.dart'; +import 'StatementNode.dart'; + +class ForInStatementNode extends StatementNode { + String? loopVariable; + GenericStatementNode? iterable; + String? body; + + @override + String toSource() { + return ''' + for (let $loopVariable in ${iterable?.toSource()}) { + $body + } + '''; + } +} diff --git a/dart2js/lib/src/node/ForStatementNode.dart b/dart2js/lib/src/node/ForStatementNode.dart new file mode 100644 index 00000000..5d216fc6 --- /dev/null +++ b/dart2js/lib/src/node/ForStatementNode.dart @@ -0,0 +1,19 @@ +import '../funs/removeCommaAsNeeded.dart'; +import 'StatementNode.dart'; +import '../funs/addCommaAsNeeded.dart'; + +class ForStatementNode extends StatementNode { + String initExpr = ''; + String? conditionalExpr; + String stepExpr = ''; + String? body; + + @override + String toSource() { + return ''' + for (${initExpr.isEmpty ? ';' : initExpr} ${addCommaAsNeeded(conditionalExpr)} ${removeCommaAsNeeded(stepExpr)}) { + $body + } + '''; + } +} \ No newline at end of file diff --git a/dart2js/lib/src/node/FunctionBodyNode.dart b/dart2js/lib/src/node/FunctionBodyNode.dart new file mode 100644 index 00000000..3fd2cb49 --- /dev/null +++ b/dart2js/lib/src/node/FunctionBodyNode.dart @@ -0,0 +1,13 @@ +import 'StatementNode.dart'; + +class FunctionBodyNode { + List statements = []; + + void push(StatementNode node) { + statements.add(node); + } + + String toSource() { + return statements.map((e) => e.toSource()).join('\r\n'); + } +} \ No newline at end of file diff --git a/dart2js/lib/src/node/FunctionDeclarationNode.dart b/dart2js/lib/src/node/FunctionDeclarationNode.dart new file mode 100644 index 00000000..49b3b9c0 --- /dev/null +++ b/dart2js/lib/src/node/FunctionDeclarationNode.dart @@ -0,0 +1,114 @@ +import 'FunctionBodyNode.dart'; +import '../utils/const.dart'; + +class FunctionDeclarationNode { + String? name; + String? className; + List> argumentList = []; + List> namedArgumentList = []; + List> optionalArgumentList = []; + bool isAsync = false; + bool isGenerativeConstructor = false; + bool isRedirectConstructor = false; + bool withContext = false; + bool classHasStaticFields = false; + bool isStatic = false; + FunctionBodyNode body = FunctionBodyNode(); + static final argContext = '__arg_ctx__'; + + String argumentObj() { + if (argumentList.isEmpty && + namedArgumentList.isEmpty && + optionalArgumentList.isEmpty) { + return ''; + } + var res = StringBuffer('{'); + if (argumentList.isNotEmpty) { + res + ..write(argumentList.map((e) => e[0]).join(',')) + ..write(','); + } + if (namedArgumentList.isNotEmpty) { + res + ..write(namedArgumentList.map((e) => e[0]).join(',')) + ..write(','); + } + if (optionalArgumentList.isNotEmpty) { + res + ..write(optionalArgumentList.map((e) => e[0]).join(',')) + ..write(','); + } + res.write('}'); + return res.toString(); + } + + String withArgumentObjAsNeeded(String body) { + if (argumentList.isEmpty && + namedArgumentList.isEmpty && + optionalArgumentList.isEmpty) { + return body; + } + + return ''' + with ($argContext) { + $body + } + '''; + } + + String withClassAsNeeded(String body) { + if (!classHasStaticFields || isStatic) { + return body; + } + return ''' + with($className) { + $body + } + '''; + } + + String wrapBodyWithCtx() { + var argObj = argumentObj(); + if (isRedirectConstructor) { + return ''' + ${argObj.isNotEmpty ? 'const $argContext = $argObj;' : ''} + ${withArgumentObjAsNeeded(body.toSource())} + '''; + } + return ''' + const $thisAlias = ${!isGenerativeConstructor ? 'this' : 'Object.create($className.prototype)'}; + ${argObj.isNotEmpty ? 'const $argContext = $argObj;' : ''} + ${!isGenerativeConstructor ? '' : '$className.$innerName.call($thisAlias);'} + ${withClassAsNeeded(''' + with ($thisAlias) { + ${withArgumentObjAsNeeded(isGenerativeConstructor ? '(function() {${body.toSource()}}).call($thisAlias);' : body.toSource())} + } + ''')} + ${!isGenerativeConstructor ? '' : 'return $thisAlias;'} + '''; + } + + String toSource() { + var namedArgs = ''; + if (namedArgumentList.isNotEmpty) { + if (argumentList.isNotEmpty) { + namedArgs = ','; + } + namedArgs += + '''{${namedArgumentList.map((e) => e[0] + (e.length == 2 ? '=' + e[1] : '')).join(',')}}={}'''; + } + var optionalArgs = ''; + if (optionalArgumentList.isNotEmpty) { + if (namedArgumentList.isNotEmpty || argumentList.isNotEmpty) { + optionalArgs = ','; + } + optionalArgs += + '''${optionalArgumentList.map((e) => e[0] + (e.length == 2 ? '=' + e[1] : '')).join(',')}'''; + } + + var finalBody = withContext ? wrapBodyWithCtx() : body.toSource(); + return '''${isAsync ? 'async ' : ''}function $name(${argumentList.map((elem) => elem[0]).join(',')}$namedArgs$optionalArgs) { + $finalBody + }'''; + } +} diff --git a/dart2js/lib/src/node/GenericStatementNode.dart b/dart2js/lib/src/node/GenericStatementNode.dart new file mode 100644 index 00000000..84614d5b --- /dev/null +++ b/dart2js/lib/src/node/GenericStatementNode.dart @@ -0,0 +1,15 @@ +// 原样输出的语句 +import 'StatementNode.dart'; + +class GenericStatementNode extends StatementNode { + String? code; + + GenericStatementNode(String code_) { + code = code_; + } + + @override + String toSource() { + return code ?? ''; + } +} \ No newline at end of file diff --git a/dart2js/lib/src/node/IfStatementNode.dart b/dart2js/lib/src/node/IfStatementNode.dart new file mode 100644 index 00000000..87f4d013 --- /dev/null +++ b/dart2js/lib/src/node/IfStatementNode.dart @@ -0,0 +1,32 @@ +import 'StatementNode.dart'; + +class IfStatementNode extends StatementNode { + String? condition; + String? thenBody; + String? lastElseBody; + IfStatementNode? elseBody; + + @override + String toSource() { + var finalElseBody = ''; + if (elseBody != null) { + finalElseBody = ''' + else ${elseBody?.toSource()} + '''; + } else if (lastElseBody != null) { + finalElseBody = lastElseBody?.isEmpty == true + ? '' + : ''' + else { + $lastElseBody + } + '''; + } + + return ''' + if ($condition) { + $thenBody + } $finalElseBody + '''; + } +} \ No newline at end of file diff --git a/dart2js/lib/src/node/IndexExpressionNode.dart b/dart2js/lib/src/node/IndexExpressionNode.dart new file mode 100644 index 00000000..b3664534 --- /dev/null +++ b/dart2js/lib/src/node/IndexExpressionNode.dart @@ -0,0 +1,17 @@ +import '../utils/const.dart'; +import 'GenericStatementNode.dart'; +import 'StatementNode.dart'; + +class IndexExpressionNode extends StatementNode { + GenericStatementNode? key; + GenericStatementNode? target; + var isSet = false; + GenericStatementNode? value; + + @override + String toSource() { + return isSet + ? '''${target?.toSource()}.${OperatorOverloadSymbol.indexEqual}(${key?.toSource()}, ${value?.toSource()});''' + : '''${target?.toSource()}.${OperatorOverloadSymbol.index}(${key?.toSource()})'''; + } +} \ No newline at end of file diff --git a/dart2js/lib/src/node/ListLiteralStatementNode.dart b/dart2js/lib/src/node/ListLiteralStatementNode.dart new file mode 100644 index 00000000..c5d37a85 --- /dev/null +++ b/dart2js/lib/src/node/ListLiteralStatementNode.dart @@ -0,0 +1,40 @@ +import 'package:analyzer/dart/ast/ast.dart'; + +import '../covert/convertExpression.dart'; +import 'StatementNode.dart'; +import 'GenericStatementNode.dart'; + +class ListLiteralStatementNode extends StatementNode { + List elements = []; + static const elsePlaceholder = '"__if_element_placeholder__"'; + var hasPlaceholder = false; + + void addElement(CollectionElement e) { + var code = ''; + if (e is SpreadElement) { + code = '...${convertExpression(e.expression.toString())}'; + } else if (e is IfElement) { + hasPlaceholder = e.elseElement == null; + var elseElem = hasPlaceholder + ? elsePlaceholder + : convertExpression(e.elseElement.toString()); + code = + '(${convertExpression(e.condition.toString())}) ? ${convertExpression(e.thenElement.toString())} : $elseElem'; + } else { + code = convertExpression(e.toString()); + } + elements.add(GenericStatementNode(code)); + } + + @override + String toSource() { + return !hasPlaceholder + ? '[${elements.map((e) => e.toString()).join(",")}]' + : ''' + (function() { + let __arr__ = [${elements.map((e) => e.toString()).join(",")}]; + return __arr__.filter(elem => elem !== $elsePlaceholder); + })(); + '''; + } +} \ No newline at end of file diff --git a/dart2js/lib/src/node/MemberAccessStatementNode.dart b/dart2js/lib/src/node/MemberAccessStatementNode.dart new file mode 100644 index 00000000..e55f94dc --- /dev/null +++ b/dart2js/lib/src/node/MemberAccessStatementNode.dart @@ -0,0 +1,5 @@ +import 'StatementNode.dart'; + +class MemberAccessStatementNode extends StatementNode { + String? thiz; +} \ No newline at end of file diff --git a/dart2js/lib/src/node/MethodInvokeStatementNode.dart b/dart2js/lib/src/node/MethodInvokeStatementNode.dart new file mode 100644 index 00000000..af5d7b26 --- /dev/null +++ b/dart2js/lib/src/node/MethodInvokeStatementNode.dart @@ -0,0 +1,37 @@ +import '../utils/const.dart'; +import 'MemberAccessStatementNode.dart'; +class MethodInvokeStatementNode extends MemberAccessStatementNode { + String? methodName; + List unnamedParameters = []; + List> namedParameters = []; + String? parentClassName; + + @override + String toSource() { + var finalNamedParameters = StringBuffer(); + if (unnamedParameters.isNotEmpty && namedParameters.isNotEmpty) { + finalNamedParameters.write(','); + } + if (namedParameters.isNotEmpty) { + finalNamedParameters.write('{'); + finalNamedParameters + .write(namedParameters.map((e) => '''${e[0]}:${e[1]}''').join(',')); + finalNamedParameters.write('}'); + } + + if (thiz != null && thiz?.trim() == superSubstitution) { + var params = + '${unnamedParameters.join(',')}${finalNamedParameters.toString()}'; + if (parentClassName == null || parentClassName!.isEmpty) { + return ''; + } + return '$parentClassName.prototype.$methodName.call(this${params.isEmpty ? '' : ',$params'});'; + } + if (transpileOption.modifySetState && methodName == setStateMethodName) { + unnamedParameters.insert(0, "'$FairKeyPlaceholder'"); + } + return ''' + ${thiz != null && thiz!.isNotEmpty ? thiz! + '.' : ''}$methodName(${unnamedParameters.join(',')}${finalNamedParameters.toString()}); + '''; + } +} \ No newline at end of file diff --git a/dart2js/lib/src/node/NewOperatorStatementNode.dart b/dart2js/lib/src/node/NewOperatorStatementNode.dart new file mode 100644 index 00000000..9fc38a82 --- /dev/null +++ b/dart2js/lib/src/node/NewOperatorStatementNode.dart @@ -0,0 +1,16 @@ +import 'MethodInvokeStatementNode.dart'; + +/** + * + * thiz 必然为null + * + */ +class NewOperatorStatementNode extends MethodInvokeStatementNode { + @override + String toSource() { + if (thiz != null) { + throw Exception('thiz must be null for NewOperatorStatementNode'); + } + return 'new ${super.toSource()}'; + } +} \ No newline at end of file diff --git a/dart2js/lib/src/node/ParenthesizedExpressionNode.dart b/dart2js/lib/src/node/ParenthesizedExpressionNode.dart new file mode 100644 index 00000000..5e8b42df --- /dev/null +++ b/dart2js/lib/src/node/ParenthesizedExpressionNode.dart @@ -0,0 +1,10 @@ +import 'GenericStatementNode.dart'; + +class ParenthesizedExpressionNode extends GenericStatementNode { + ParenthesizedExpressionNode(String code_) : super(code_); + + @override + String toSource() { + return '''($code)'''; + } +} \ No newline at end of file diff --git a/dart2js/lib/src/node/PrefixExpressionNode.dart b/dart2js/lib/src/node/PrefixExpressionNode.dart new file mode 100644 index 00000000..dd381c3d --- /dev/null +++ b/dart2js/lib/src/node/PrefixExpressionNode.dart @@ -0,0 +1,12 @@ +import 'GenericStatementNode.dart'; +import 'StatementNode.dart'; + +class PrefixExpressionNode extends StatementNode { + String? operator; + GenericStatementNode? operand; + + @override + String toSource() { + return '''$operator${operand?.toSource()}'''; + } +} \ No newline at end of file diff --git a/dart2js/lib/src/node/PropertyAccessStatementNode.dart b/dart2js/lib/src/node/PropertyAccessStatementNode.dart new file mode 100644 index 00000000..c8435272 --- /dev/null +++ b/dart2js/lib/src/node/PropertyAccessStatementNode.dart @@ -0,0 +1,16 @@ +import 'MemberAccessStatementNode.dart'; +import '../utils/const.dart'; + +class PropertyAccessStatementNode extends MemberAccessStatementNode { + String? fieldName; + String? setVal; + + @override + String toSource() { + var finalThiz = + thiz != null && thiz!.trim() == superSubstitution ? 'this' : thiz; + return ''' + ${finalThiz?.isNotEmpty == true ? finalThiz! + '.' : ''}$fieldName${setVal == null ? '' : '=' + setVal! + ';'} + '''; + } +} diff --git a/dart2js/lib/src/node/ReturnStatementNode.dart b/dart2js/lib/src/node/ReturnStatementNode.dart new file mode 100644 index 00000000..797608ad --- /dev/null +++ b/dart2js/lib/src/node/ReturnStatementNode.dart @@ -0,0 +1,19 @@ +import 'StatementNode.dart'; + +class ReturnStatementNode extends StatementNode { + String? expr; + + @override + String toSource() { + String exprR = expr ?? ""; + if (exprR != null && exprR.isNotEmpty) { + if (exprR.startsWith("Future(")) { + exprR = exprR.replaceAll("Future(", "Promise.resolve().then("); + } + } + + return ''' + return $exprR${!(exprR?.endsWith(';') ?? false) ? ';' : ''} + '''; + } +} \ No newline at end of file diff --git a/dart2js/lib/src/node/StatementNode.dart b/dart2js/lib/src/node/StatementNode.dart new file mode 100644 index 00000000..ee677e17 --- /dev/null +++ b/dart2js/lib/src/node/StatementNode.dart @@ -0,0 +1,10 @@ +class StatementNode { + String toSource() { + return ''; + } + + @override + String toString() { + return toSource(); + } +} \ No newline at end of file diff --git a/dart2js/lib/src/node/SwitchStatementNode.dart b/dart2js/lib/src/node/SwitchStatementNode.dart new file mode 100644 index 00000000..2a35e14b --- /dev/null +++ b/dart2js/lib/src/node/SwitchStatementNode.dart @@ -0,0 +1,38 @@ +import 'GenericStatementNode.dart'; +import 'StatementNode.dart'; + +class SwitchStatementNode extends StatementNode { + GenericStatementNode? expr; + List>? cases; + String? default_; + + String genCase(List case_) { + return case_.length == 2 + ? ''' + case ${case_[0]}: + ${case_[1]} + ''' + : ''' + case ${case_[0]}: + '''; + } + + String genDefault() { + return default_ != null && default_!.isNotEmpty + ? ''' + default: + $default_ + ''' + : ''; + } + + @override + String toSource() { + return ''' + switch (${expr?.toSource()}) { + ${cases != null && cases!.isNotEmpty ? cases?.map((e) => genCase(e)).join('') : ''} + ${genDefault()} + } + '''; + } +} \ No newline at end of file diff --git a/dart2js/lib/src/node/TryStatementNode.dart b/dart2js/lib/src/node/TryStatementNode.dart new file mode 100644 index 00000000..9bdad697 --- /dev/null +++ b/dart2js/lib/src/node/TryStatementNode.dart @@ -0,0 +1,33 @@ +import 'StatementNode.dart'; + +class TryStatementNode extends StatementNode { + String? tryBody; + String? catchBody; + String? exceptionParameter; // catch(e) ,e:stackTraceParameter + String? finallyBody; + String? stackTraceParameter; // catch(e,s) ,s:stackTraceParameter + + @override + String toSource() { + String tryStr = '''try { + $tryBody + }'''; + + String catchStr = ''' catch(${exceptionParameter}){ + ${catchBody} + }'''; + + String finallyStr = ''' finally { + ${finallyBody} + }'''; + + String result = tryStr; + if (catchBody != null) { + result = result + catchStr; + } + if (finallyBody != null) { + result = result + finallyStr; + } + return result; + } +} diff --git a/dart2js/lib/src/node/WhileStatementNode.dart b/dart2js/lib/src/node/WhileStatementNode.dart new file mode 100644 index 00000000..6a5d199b --- /dev/null +++ b/dart2js/lib/src/node/WhileStatementNode.dart @@ -0,0 +1,20 @@ +import 'StatementNode.dart'; + +class WhileStatementNode extends StatementNode { + String? condition; + String? body; + + @override + String toSource() { + var finalBody = body == null || body?.isEmpty == true + ? ';' + : ''' + { + $body + } + '''; + return ''' + while ($condition) $finalBody + '''; + } +} \ No newline at end of file diff --git a/dart2js/lib/src/utils/const.dart b/dart2js/lib/src/utils/const.dart new file mode 100644 index 00000000..2780e588 --- /dev/null +++ b/dart2js/lib/src/utils/const.dart @@ -0,0 +1,87 @@ + +import 'package:path/path.dart' as p; +const superSubstitution = '__super__'; +const argsName = '__args__'; +const innerName = '__inner__'; +const constructorAlias = 'ctor'; +const factoryConstructorAlias = '__fty__ctor__'; +const thisAlias = '__thiz__'; +const setStateMethodName = 'setState'; +const FairKeyPlaceholder = '#FairKey#'; +const commonJs = ''' +var __global__ = global; +global.int = { + parse: function(source, {radix} = {}) { + return parseInt(source, radix || 10); + } +}; +var print = console.log.bind(console); +function inherit(cls, sup) { + var oldProto = cls.prototype; + cls.prototype = Object.create(Object.create(sup.prototype)); + Object.assign(cls.prototype, oldProto); + cls.prototype.constructor = cls; + cls.prototype.$superSubstitution = cls.prototype.__proto__; +} + +function convertObjectLiteralToSetOrMap(obj) { + let isSet = Object.prototype.toString.call(obj) == '[object Array]'; + if (!isSet) { + let keys = Object.getOwnPropertyNames(obj); + let res = new Map(); + keys.forEach(k => res.set(k, obj[k])); + return res; + } else { + let res = new Set(); + obj.forEach(item => res.add(item)); + return res; + } +} + +Object.prototype.ctor = function(){}; +Object.__inner__ = function(){}; +'''; + +class TranspileOption { + bool modifySetState = false; +} + +var transpileOption = TranspileOption(); + +class Tuple { + K1? k1; + K2? k2; + K3? k3; + + Tuple(this.k1, this.k2, this.k3); +} +class OperatorOverloadSymbol { + static final add = '__op_add__'; + static final minus = '__op_minus__'; + static final negative = '__op_ngt__'; + static final multi = '__op_multi__'; + static final idivide = '__op_idivide__'; + static final lessThan = '__op_lt__'; + static final greaterThan = '__op_gt__'; + static final lessEqualThan = '__op_lte__'; + static final greaterEqualThan = '__op_gte__'; + static final equal = '__op_eq__'; + static final index = '__op_idx__'; + static final indexEqual = '__op_idxeq__'; +} +enum ClassOutputTemplateType { raw, pageState } + +String resolvePath(basePath, relativePath) { + var p1 = p.split(basePath); + var p2 = p.split(relativePath); + for (var segment in p2) { + if (segment == '..') { + p1.removeLast(); + } else if (segment == '.') { + continue; + } else { + p1.add(segment); + } + } + return p.joinAll(p1); +} \ No newline at end of file diff --git a/dart2js/lib/src/utils/uglify.dart b/dart2js/lib/src/utils/uglify.dart new file mode 100644 index 00000000..4fa2bf84 --- /dev/null +++ b/dart2js/lib/src/utils/uglify.dart @@ -0,0 +1,55 @@ +String uglify(String str) { + // 不用处理注释 + var buf = StringBuffer(); + var isInString = false; + var lastCh = ''; + void writeCh(String s) { + buf.write(s); + lastCh = s; + assert(!(s == '\r' || s == '\n')); + } + + for (var i = 0; i < str.length; i++) { + if (isInString) { + writeCh(str[i]); + if ((str[i] == '\'' || str[i] == '"' || str[i] == '`') && + (i < 1 || str[i - 1] != '\\')) { + isInString = false; + } + } else { + if ((str[i] == '\'' || str[i] == '"' || str[i] == '`') && + (str[i - 1] != '\\')) { + isInString = true; + writeCh(str[i]); + } else { + if (str[i] == '\r' || str[i] == '\n') { + continue; + } else if (str[i] == ' ') { + do { + i++; + } while (i < str.length && str[i] == ' '); + if (buf.isNotEmpty) { + if (i < str.length) { + var pat = RegExp(r'^[a-zA-Z_\d]$'); + if (pat.hasMatch(lastCh) && pat.hasMatch(str[i])) { + writeCh(' '); + } + i--; + } else { + break; + } + } else { + if (i < str.length) { + i--; + } else { + break; + } + } + } else { + writeCh(str[i]); + } + } + } + } + return buf.toString(); +} diff --git a/dart2js/lib/src/visitor/ClassDeclarationVisitor.dart b/dart2js/lib/src/visitor/ClassDeclarationVisitor.dart new file mode 100644 index 00000000..cafb136f --- /dev/null +++ b/dart2js/lib/src/visitor/ClassDeclarationVisitor.dart @@ -0,0 +1,192 @@ +import 'dart:io'; + +import 'package:analyzer/dart/analysis/features.dart'; +import 'package:analyzer/dart/analysis/utilities.dart'; +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/ast/visitor.dart'; + +import 'SimpleFunctionGenerator.dart'; +import '../covert/convertExpression.dart'; +import '../declaration/ClassDeclarationData.dart'; +import '../declaration/FieldDeclarationData.dart'; +import '../declaration/MethodDeclarationData.dart'; +import '../utils/const.dart'; +import 'UniqueNameGenerator.dart'; + +class ClassDeclarationVisitor + extends RecursiveAstVisitor { + List classes = []; + bool isDataBean; + List getterList = []; + + ClassDeclarationVisitor([this.isDataBean = false]); + + @override + ClassDeclarationVisitor? visitClassDeclaration(ClassDeclaration node) { + var classDeclarationData = ClassDeclarationData(); + classDeclarationData.isDataBean = isDataBean; + classDeclarationData.className = node.name.name; + if (node.extendsClause != null) { + classDeclarationData.parentClass = + node.extendsClause!.superclass.toString(); + } + node.members.forEach((element) { + if (element is FieldDeclaration) { + var fieldDeclaration = + element.fields.variables.first.toString().split('='); + classDeclarationData.fields.add(FieldDeclarationData( + fieldDeclaration[0].trim(), + fieldDeclaration.length == 2 + ? convertExpression(fieldDeclaration[1].trim()) + : null) + ..isStatic = element.isStatic); + } else if (element is MethodDeclaration) { + if (isDataBean && ['fromJson', 'toJson'].contains(element.name.name)) { + return; + } + if (element.isGetter) { + getterList.add(element); + return; + } + // var fairWellExp = RegExp(r"@FairWell\('(.+)'\)"); + // if (fairWellExp.allMatches(element.metadata.first.toString()).isNotEmpty) { + var excludeMethods = ['build']; + if (!excludeMethods.contains(element.name.name)) { + classDeclarationData.methods.add(MethodDeclarationData( + element.name.name, + '${element.returnType.toString()} ${element.name.name}${element.parameters.toString()}${element.body.toString()}', + element.body is ExpressionFunctionBody) + ..isStatic = element.isStatic); + } + // } + } else if (element is ConstructorDeclaration) { + if (isDataBean && ['fromJson', 'toJson'].contains(element.name?.name)) { + return; + } + var idx = 0; + var constructorBody = element.body.toString(); + if (element.body is EmptyFunctionBody) { + constructorBody = '{}'; + } + constructorBody = + '$constructorAlias${element.parameters.toString()}$constructorBody'; + var methodDeclaration = MethodDeclarationData( + element.name?.name ?? constructorAlias, + constructorBody, + element.body is ExpressionFunctionBody); + if (element.factoryKeyword?.stringValue == 'factory') { + methodDeclaration.isFactory = true; + methodDeclaration.name = (element.name == null) + ? factoryConstructorAlias + : element.name!.name; + } else { + methodDeclaration.isGenerativeConstructor = element.name != null; + } + element.parameters.parameters.forEach((element) { + if (element is FieldFormalParameter) { + var newParamName = UniqueNameGenerator().next(); + methodDeclaration + ..renamedParameters[idx] = newParamName + ..abtractedInitializer + .add('${element.toString()} = $newParamName;'); + } else if (element is DefaultFormalParameter) { + if (element.parameter is FieldFormalParameter) { + var newParamName = element.parameter.identifier.toString(); + methodDeclaration + ..renamedParameters[idx] = newParamName + ..abtractedInitializer + .add('${element.parameter.toString()} = $newParamName;'); + } + } + idx++; + }); + var callSuperConstructorImplicitly = false; + element.initializers.forEach((initializer) { + if (initializer is SuperConstructorInvocation) { + callSuperConstructorImplicitly = true; + var params = initializer.argumentList.arguments + .map((e) => e.toString()) + .join(','); + methodDeclaration.abtractedInitializer.add( + '${classDeclarationData.parentClass}.prototype.$constructorAlias.call($thisAlias${params.isEmpty ? '' : ',$params'});'); + } else if (initializer is ConstructorFieldInitializer) { + methodDeclaration.abtractedInitializer.add( + 'this.${initializer.fieldName} = ${convertExpression(initializer.expression.toString())};'); + } else if (initializer is RedirectingConstructorInvocation) { + var params = initializer.argumentList.arguments + .map((e) => convertExpression(e.toString())) + .join(','); + methodDeclaration.abtractedInitializer + .add('return new ${element.returnType.name}($params);'); + methodDeclaration.isRedirectConstructor = true; + } else { + throw 'Unhandled constructor element ${element.toString()}'; + } + }); + if (!callSuperConstructorImplicitly && + !methodDeclaration.isRedirectConstructor && + !methodDeclaration.isFactory) { + methodDeclaration.abtractedInitializer.insert(0, + '${classDeclarationData.parentClass == null || classDeclarationData.parentClass!.isEmpty ? 'Object' : classDeclarationData.parentClass}.prototype.$constructorAlias.call($thisAlias);'); + } + classDeclarationData.methods.add(methodDeclaration); + } + }); + if (getterList.length > 0) { + getterList.forEach((element) { + var elementBody = element.body.toString(); + if (element.body is EmptyFunctionBody) { + elementBody = '{}'; + } + if (element.body is ExpressionFunctionBody) { + classDeclarationData.fields.add(FieldDeclarationData( + element.name.name.trim(), + "return ${convertExpression(element.body.toString().replaceAll("=>", "").trim())};") + ..isStatic = element.isStatic + ..isGetter = true); + } else { + var methodDeclaration = MethodDeclarationData( + element.name.name.trim(), + "void test() ${elementBody}", + element.body is ExpressionFunctionBody); + var res = parseString(content: methodDeclaration.body); + + var generator = SimpleFunctionGenerator( + isArrowFunc: false, + renamedParameters: methodDeclaration.renamedParameters, + parentClass: classDeclarationData?.parentClass); + + generator.func + ?..withContext = true + ..classHasStaticFields = element.isStatic; + res.unit.visitChildren(generator); + + classDeclarationData.fields.add(FieldDeclarationData( + element.name.name, generator.func?.body.toSource()) + ..isStatic = element.isStatic + ..isGetter = true); + } + return; + }); + } + classes.add(classDeclarationData); + return null; + } + + void parseByFile(String filePath) { + var file = File(filePath); + var result = parseFile( + path: file.absolute.uri.normalizePath().path, + featureSet: FeatureSet.fromEnableFlags([])); + result.unit.visitChildren(this); + } + + void parseByString(String content) { + var res = parseString(content: content); + res.unit.visitChildren(this); + } + + String genJsCode() { + return classes.map((e) => e.genJsCode()).join('\r\n'); + } +} diff --git a/dart2js/lib/src/visitor/PartJsCodeGenerator.dart b/dart2js/lib/src/visitor/PartJsCodeGenerator.dart new file mode 100644 index 00000000..6e305e4c --- /dev/null +++ b/dart2js/lib/src/visitor/PartJsCodeGenerator.dart @@ -0,0 +1,38 @@ + +import 'dart:io'; + +import 'package:analyzer/dart/analysis/features.dart'; +import 'package:analyzer/dart/analysis/utilities.dart'; +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/ast/visitor.dart'; + +import '../utils/const.dart'; + +class PartJsCodeGenerator extends SimpleAstVisitor { + // libPath, isDataBean, moduleAlias + List> importLocalFiles = + >[]; + var BeanDir = '/bean/'; + var PackagePrefix = new RegExp('(dart|package):'); + + @override + PartJsCodeGenerator? visitImportDirective(ImportDirective node) { + var libraryPath = node.uri.stringValue; + if (libraryPath?.contains(BeanDir) == true) { + importLocalFiles.add(Tuple(libraryPath, true, node.prefix?.toSource())); + } else if (!(libraryPath?.startsWith(PackagePrefix) ?? false)) { + importLocalFiles.add(Tuple(libraryPath, false, node.prefix?.toSource())); + } + return null; + } + + void parse(String filePath) { + var file = File(filePath); + var result = parseFile( + path: Platform.isWindows + ? filePath + : file.absolute.uri.normalizePath().path, + featureSet: FeatureSet.fromEnableFlags([])); + result.unit.visitChildren(this); + } +} \ No newline at end of file diff --git a/dart2js/lib/src/visitor/SimpleFunctionGenerator.dart b/dart2js/lib/src/visitor/SimpleFunctionGenerator.dart new file mode 100644 index 00000000..fb4836be --- /dev/null +++ b/dart2js/lib/src/visitor/SimpleFunctionGenerator.dart @@ -0,0 +1,114 @@ +import 'dart:collection'; + +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/ast/visitor.dart'; + +import '../node/ArrowFunctionExpressionNode.dart'; +import '../node/FunctionDeclarationNode.dart'; +import 'handle/handleBreakStatement.dart'; +import 'handle/handelExpressionStatement.dart'; +import 'handle/handleContinueStatement.dart'; +import 'handle/handleDoStatement.dart'; +import 'handle/handleExpressionFunctionBody.dart'; +import 'handle/handleForStatement.dart'; +import 'handle/handleFunctionDeclarationStatement.dart'; +import 'handle/handleIfStatement.dart'; +import 'handle/handleReturnStatement.dart'; +import 'handle/handleSwitchStatement.dart'; +import 'handle/handleTryStatement.dart'; +import 'handle/handleVariableDeclarationStatement.dart'; +import 'handle/handleWhileStatement.dart'; + +class SimpleFunctionGenerator + extends GeneralizingAstVisitor { + FunctionDeclarationNode? func; + String? parentClass; + HashMap? renamedParameters; + + SimpleFunctionGenerator( + {bool isArrowFunc = false, this.renamedParameters, this.parentClass}) { + func = + isArrowFunc ? ArrowFunctionExpressionNode() : FunctionDeclarationNode(); + } + + @override + SimpleFunctionGenerator? visitFunctionDeclaration(FunctionDeclaration node) { + func?.name = node.name.toString(); + return super.visitFunctionDeclaration(node); + } + + @override + SimpleFunctionGenerator? visitFormalParameterList(FormalParameterList node) { + var idx = 0; + node.parameters.forEach((param) { + var ident = param.identifier.toString(); + if (renamedParameters != null && renamedParameters!.containsKey(idx)) { + ident = renamedParameters![idx]!; + } + var arg = [ident]; + + if (param.isNamed) { + if (param is DefaultFormalParameter && (param.defaultValue != null)) { + arg.add(param.defaultValue.toString()); + } + func?.namedArgumentList.add(arg); + } else if (param.isOptional) { + if (param is DefaultFormalParameter && (param.defaultValue != null)) { + arg.add(param.defaultValue.toString()); + } + func?.optionalArgumentList.add(arg); + } else { + func?.argumentList.add(arg); + } + + idx++; + }); + return null; + } + + @override + SimpleFunctionGenerator? visitBlockFunctionBody(BlockFunctionBody node) { + func?.isAsync = node.isAsynchronous; + return super.visitBlockFunctionBody(node); + } + + @override + SimpleFunctionGenerator? visitNode(AstNode node) { + if (node is ExpressionStatement) { + handleExpressionStatement(node, func, parentClass); + } else if (node is ExpressionFunctionBody) { + handleExpressionFunctionBody(node, func); + } else if (node is VariableDeclarationStatement) { + handleVariableDeclarationStatement(node, func); + } else if (node is WhileStatement) { + handleWhileStatement(node, func); + } else if (node is DoStatement) { + handleDoStatement(node, func); + } else if (node is IfStatement) { + handleIfStatement(node, func); + } else if (node is SwitchStatement) { + handleSwitchStatement(node, func); + } else if (node is ReturnStatement) { + handleReturnStatement(node, func); + } else if (node is ForStatement) { + handleForStatement(node, func); + } else if (node is ContinueStatement) { + handleContinueStatement(node, func); + } else if (node is BreakStatement) { + handleBreakStatement(node, func); + } else if (node is FunctionDeclarationStatement) { + handleFunctionDeclarationStatement(node, func); + } else if (node is TryStatement) { + // done 2022-10-8 + handleTryStatement(node,func); + } else if (node is EmptyStatement) { + // done 2022-10-8 代码为 ";" 这种情况无需处理忽略即可 + } else if (node is LabeledStatement) { + // todo 尚未实现 + } else if (node is YieldStatement) { + // todo 尚未实现 + } else { + return super.visitNode(node); + } + } +} diff --git a/dart2js/lib/src/visitor/UniqueNameGenerator.dart b/dart2js/lib/src/visitor/UniqueNameGenerator.dart new file mode 100644 index 00000000..f92d0097 --- /dev/null +++ b/dart2js/lib/src/visitor/UniqueNameGenerator.dart @@ -0,0 +1,17 @@ +class UniqueNameGenerator { + static final _instance = UniqueNameGenerator._internal(); + int seq = 0; + + factory UniqueNameGenerator() => _instance; + + void reset() { + _instance.seq = 0; + } + + String next() { + seq++; + return '__temp_f_$seq\_\_'; + } + + UniqueNameGenerator._internal(); +} \ No newline at end of file diff --git a/dart2js/lib/src/visitor/WidgetStateGenerator.dart b/dart2js/lib/src/visitor/WidgetStateGenerator.dart new file mode 100644 index 00000000..10e7577e --- /dev/null +++ b/dart2js/lib/src/visitor/WidgetStateGenerator.dart @@ -0,0 +1,248 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/ast/visitor.dart'; +import 'package:fair_dart2js/src/list_ext.dart'; + +import '../covert/convertExpression.dart'; +import '../declaration/ClassDeclarationData.dart'; +import 'ClassDeclarationVisitor.dart'; +import '../declaration/FieldDeclarationData.dart'; +import '../declaration/MethodDeclarationData.dart'; +import '../utils/const.dart'; +import 'package:path/path.dart' as p; + +import 'PartJsCodeGenerator.dart'; + +class WidgetStateGenerator extends RecursiveAstVisitor { + static var FairPropsAttribute = 'FairProps'; + static var InitPropsName = '__initProps__'; + var baseFilePath = ''; + var moduleSequence = 1; + Map dependencyCache = {}; // module path / module sequence + + var classDeclarationData = ClassDeclarationData(); + var allStates = []; + var allInnerDataClasses = []; + + WidgetStateGenerator(this.baseFilePath); + + void goThroughMembers( + ClassDeclaration node, ClassDeclarationData tempClassDeclaration) { + node.members.forEach((element) { + if (element is FieldDeclaration) { + var fieldDeclaration = + element.fields.variables.first.toString().split('='); + var hasFairPropsAttribute = element.metadata + .any((elem) => elem.name.toString() == FairPropsAttribute); + tempClassDeclaration.fields.add(FieldDeclarationData( + fieldDeclaration[0].trim(), + fieldDeclaration.length == 2 + ? convertExpression(fieldDeclaration[1].trim()) + : hasFairPropsAttribute + ? InitPropsName + : null)); + } else if (element is MethodDeclaration) { + // var fairWellExp = RegExp(r"@FairWell\('(.+)'\)"); + // if (fairWellExp.allMatches(element.metadata.first.toString()).isNotEmpty) { + var excludeMethods = ['build']; + if (!excludeMethods.contains(element.name.toString()) && + element.returnType.toString() != 'Widget') { + tempClassDeclaration.methods.add(MethodDeclarationData( + element.name.toString(), + element.toString(), + element.body is ExpressionFunctionBody) + ..isStatic = element.isStatic); + } + // } + } + }); + } + + String findCreateStateReturn(FunctionBody body) { + if (body is BlockFunctionBody) { + ReturnStatement? returnStatement = body.block.statements + .singleWhereOrNull((element) => element is ReturnStatement, + orElse: () => null) as ReturnStatement?; + assert(returnStatement != null, + 'too complicated createState implementation'); + assert(returnStatement?.expression is MethodInvocation, + 'too complicated return expression in method createState'); + return (returnStatement?.expression as MethodInvocation).methodName.name; + } else if (body is ExpressionFunctionBody) { + assert(body.expression is MethodInvocation, + 'too complicated return expression in method createState'); + return (body.expression as MethodInvocation).methodName.name; + } else { + throw 'Unsupported body in method createState'; + } + } + + @override + WidgetStateGenerator? visitClassDeclaration(ClassDeclaration node) { + var stateExp = RegExp(r'^State(<.+>)?$'); + + var tempClassDeclaration = ClassDeclarationData(); + tempClassDeclaration.className = node.name.name; + goThroughMembers(node, tempClassDeclaration); + if (node.extendsClause != null && + stateExp + .allMatches(node.extendsClause!.superclass.toString()) + .isNotEmpty) { + allStates.add(tempClassDeclaration); + if (classDeclarationData.className != null && + classDeclarationData.className == tempClassDeclaration.className) { + classDeclarationData = tempClassDeclaration; + } + } else if (node.extendsClause == null || + node.extendsClause!.superclass.toString() == 'Object') { + allInnerDataClasses.add(tempClassDeclaration); + } + var fairPatchAttributeName = 'FairPatch'; + const statefulWidgetClassName = 'StatefulWidget'; + const statelessWidgetClassName = 'StatelessWidget'; + if (node.metadata.isNotEmpty && + node.metadata + .any((item) => item.name.toString() == fairPatchAttributeName)) { + if (node.extendsClause != null) { + switch (node.extendsClause!.superclass.toString()) { + case statefulWidgetClassName: + var member = node.members.firstWhereOrNull( + (element) => + element is MethodDeclaration && + element.name.toString() == 'createState', + orElse: () => null); + if (member != null) { + var expectedStateClassName = + ((member as MethodDeclaration).returnType as TypeName) + .name + .name; + if (expectedStateClassName == 'State') { + expectedStateClassName = findCreateStateReturn(member.body); + } + var data = allStates.firstWhere( + (element) => element?.className == expectedStateClassName, + orElse: () => null); + if (data != null) { + classDeclarationData = data; + } else { + classDeclarationData.className = expectedStateClassName; + } + } else { + throw 'method createState is not found in class ${node.name.toString()}'; + } + break; + case statelessWidgetClassName: + classDeclarationData.className = node.name.name; + goThroughMembers(node, classDeclarationData); + break; + default: + // nothing to do + break; + } + } + } + return null; + } + + void generateDependencies(String refererPath, + List> imports, List result) { + if (imports.isEmpty) { + return; + } + + var dependencySequences = reserveSequence(imports.length); + var index = 0; + imports.forEach((element) { + var absPath = resolvePath(p.dirname(refererPath), element.k1); + if (dependencyCache.containsKey(absPath)) { + return; + } + dependencyCache[absPath] = dependencySequences[index]; + var partJsGenerator = PartJsCodeGenerator(); + partJsGenerator.parse(absPath); + var selfDependencySequences = []; + if (partJsGenerator.importLocalFiles.isNotEmpty) { + generateDependencies(absPath, partJsGenerator.importLocalFiles, result); + selfDependencySequences = + reserveSequence(partJsGenerator.importLocalFiles.length, true); + var index1 = 0; + partJsGenerator.importLocalFiles.forEach((element) { + var tempDependencyPath = resolvePath(p.dirname(absPath), element.k1); + if (dependencyCache.containsKey(tempDependencyPath)) { + selfDependencySequences[index1] = + dependencyCache[tempDependencyPath].toString(); + } + if (element.k3 != null && element.k3!.isNotEmpty) { + selfDependencySequences[index1] = + '[${selfDependencySequences[index1]},\'${element.k3}\']'; + } + index1++; + }); + } + var classDeclarationVisitor1 = + ClassDeclarationVisitor(element.k2 ?? false); + classDeclarationVisitor1.parseByFile(absPath); + result.add(''' + defineModule(${dependencySequences[index]}, function(__mod__) { + with (__mod__.imports) { + ${classDeclarationVisitor1.genJsCode()} + } + ${classDeclarationVisitor1.classes.map((e) => '__mod__.exports.${e.className} = ${e.className};').join('\r\n')} + }, [${selfDependencySequences.join(',')}]); + '''); + index++; + }); + } + + List reserveSequence(int num, [bool keepSequence = false]) { + var result = new List.generate(num, (index) => moduleSequence + index) + .map((item) => item.toString()) + .toList(); + if (!keepSequence) { + moduleSequence += num; + } + return result; + } + + String genJsCode() { + var dependencySequences = []; + var dependencyClasses = []; + try { + var partJsGenerator = PartJsCodeGenerator(); + partJsGenerator.parse(baseFilePath); + // parse local file dependencies + // import as? - supported + // import show / hide - not supported + // conditional export - not supported + if (partJsGenerator.importLocalFiles.isNotEmpty) { + dependencySequences = + reserveSequence(partJsGenerator.importLocalFiles.length, true); + var index = 0; + partJsGenerator.importLocalFiles.forEach((element) { + if (element.k3 != null && element.k3!.isNotEmpty) { + dependencySequences[index] = + '[${dependencySequences[index]},\'${element.k3}\']'; + } + index++; + }); + generateDependencies( + baseFilePath, partJsGenerator.importLocalFiles, dependencyClasses); + } + } catch (exception) { + print(exception); + } + classDeclarationData.outputTemplateType = ClassOutputTemplateType.raw; + return ''' + GLOBAL['$FairKeyPlaceholder'] = (function($InitPropsName) { + const __global__ = this; + ${dependencyClasses.join('\r\n')} + return runCallback(function(__mod__) { + with(__mod__.imports) { + ${allInnerDataClasses.map((e) => e.genJsCode()).join('\r\n')} + ${classDeclarationData.genJsCode()}; + return ${classDeclarationData.className}(); + } + }, [${dependencySequences.join(',')}]); + })(convertObjectLiteralToSetOrMap(JSON.parse('#FairProps#'))); + '''; + } +} diff --git a/dart2js/lib/src/visitor/handle/handelExpressionStatement.dart b/dart2js/lib/src/visitor/handle/handelExpressionStatement.dart new file mode 100644 index 00000000..edf94e65 --- /dev/null +++ b/dart2js/lib/src/visitor/handle/handelExpressionStatement.dart @@ -0,0 +1,157 @@ +import 'package:analyzer/dart/ast/ast.dart'; + +import '../../covert/convertExpression.dart'; +import 'handleStringTemplate.dart'; +import '../../node/AssignmentStatementNode.dart'; +import '../../node/AwaitStatementNode.dart'; +import '../../node/BinaryExpressionNode.dart'; +import '../../node/CascadeOperatorStatementNode.dart'; +import '../../node/ConditionalExpressionNode.dart'; +import '../../node/FunctionDeclarationNode.dart'; +import '../../node/GenericStatementNode.dart'; +import '../../node/IndexExpressionNode.dart'; +import '../../node/ListLiteralStatementNode.dart'; +import '../../node/NewOperatorStatementNode.dart'; +import '../../node/ParenthesizedExpressionNode.dart'; +import '../../node/PrefixExpressionNode.dart'; +import '../../node/PropertyAccessStatementNode.dart'; +import 'handleCreationCall.dart'; +import 'handleFuncitonExpression.dart'; +import 'handleMethodInvocation.dart'; + +void handleExpressionStatement(ExpressionStatement node, + FunctionDeclarationNode? func, String? parentClass) { + // print('ExpressionStatement:' + node.toSource()); + if (node.expression is MethodInvocation) { + func?.body.push(handleMethodInvocation( + node.expression as MethodInvocation, parentClass)); + } else if (node.expression is PropertyAccess) { + var gnNode = PropertyAccessStatementNode(); + var currentNode = node.expression as PropertyAccess; + if (currentNode.target != null) { + gnNode.thiz = convertExpression(currentNode.target.toString()); + } + gnNode.fieldName = currentNode.propertyName.toString(); + func?.body.push(gnNode); + } else if (node.expression is FunctionExpression) { + func?.body.push(GenericStatementNode( + handleFuncitonExpression(node.expression as FunctionExpression))); + } else if (node.expression is AssignmentExpression) { + var gnNode = AssignmentStatementNode(); + var currentNode = node.expression as AssignmentExpression; + if (currentNode.leftHandSide is IndexExpression) { + var gnNode1 = IndexExpressionNode(); + var currentNode1 = currentNode.leftHandSide as IndexExpression; + gnNode1.key = GenericStatementNode( + convertExpression(currentNode1.index.toString())); + gnNode1.target = GenericStatementNode( + convertExpression(currentNode1.target.toString())); + gnNode1.value = GenericStatementNode( + convertExpression(currentNode.rightHandSide.toString())); + gnNode1.isSet = true; + func?.body.push(gnNode1); + } else { + gnNode.leftSide = convertExpression(currentNode.leftHandSide.toString()); + gnNode.operator_ = currentNode.operator.toString(); + gnNode.rightSide = GenericStatementNode( + convertExpression(currentNode.rightHandSide.toString())); + func?.body.push(gnNode); + } + } else if (node.expression is CascadeExpression) { + var gnNode = CascadeOperatorStatementNode(); + var currentNode = node.expression as CascadeExpression; + gnNode.target = convertExpression(currentNode.target.toString()); + gnNode.cascades = []; + currentNode.cascadeSections.forEach((element) { + if (element is MethodInvocation) { + gnNode.cascades?.add(handleMethodInvocation(element, parentClass)); + } else if (element is AssignmentExpression) { + if (element.leftHandSide is PropertyAccess) { + var leftSide = element.leftHandSide as PropertyAccess; + if (leftSide.isCascaded) { + var assignmentNode = PropertyAccessStatementNode(); + assignmentNode.fieldName = leftSide.propertyName.toString(); + assignmentNode.setVal = + convertExpression(element.rightHandSide.toString()); + gnNode.cascades?.add(assignmentNode); + } else { + throw Exception( + '''Not supported statement(s): ${element.toString()}'''); + } + } + } else { + throw Exception( + '''Not supported statement(s): ${element.toString()}'''); + } + }); + func?.body.push(gnNode); + } else if (node.expression is ConditionalExpression) { + var gnNode = ConditionalExpressionNode(); + var currentNode = node.expression as ConditionalExpression; + gnNode.condition = GenericStatementNode( + convertExpression(currentNode.condition.toString())); + gnNode.then = GenericStatementNode( + convertExpression(currentNode.thenExpression.toString())); + gnNode.elseExpr = GenericStatementNode( + convertExpression(currentNode.elseExpression.toString())); + func?.body.push(gnNode); + } else if (node.expression is IndexExpression) { + var gnNode = IndexExpressionNode(); + var currentNode = node.expression as IndexExpression; + gnNode.key = + GenericStatementNode(convertExpression(currentNode.index.toString())); + gnNode.target = + GenericStatementNode(convertExpression(currentNode.target.toString())); + func?.body.push(gnNode); + } else if (node.expression is ParenthesizedExpression) { + var currentNode = node.expression as ParenthesizedExpression; + var gnNode = ParenthesizedExpressionNode( + convertExpression(currentNode.expression.toString())); + func?.body.push(gnNode); + } else if (node.expression is BinaryExpression) { + var currentNode = node.expression as BinaryExpression; + var gnNode = BinaryExpressionNode(); + gnNode.left = GenericStatementNode( + convertExpression(currentNode.leftOperand.toString())); + gnNode.right = GenericStatementNode( + convertExpression(currentNode.rightOperand.toString())); + gnNode.operator = currentNode.operator.toString(); + func?.body.push(gnNode); + } else if (node.expression is PrefixExpression) { + var currentNode = node.expression as PrefixExpression; + var gnNode = PrefixExpressionNode(); + gnNode.operand = + GenericStatementNode(convertExpression(currentNode.operand.toString())); + gnNode.operator = currentNode.operator.toString(); + func?.body.push(gnNode); + } else if (node.expression is IsExpression) { + var currentNode = node.expression as IsExpression; + func?.body.push(GenericStatementNode( + convertExpression(currentNode.expression.toString()))); + } else if (node.expression is AsExpression) { + var currentNode = node.expression as AsExpression; + func?.body.push(GenericStatementNode( + convertExpression(currentNode.expression.toString()))); + } else if (node.expression is AwaitExpression) { + var gnNode = AwaitStatementNode(); + var currentNode = node.expression as AwaitExpression; + gnNode.expr = GenericStatementNode( + convertExpression(currentNode.expression.toString())); + func?.body.push(gnNode); + } else if (node.expression is SingleStringLiteral) { + func?.body.push(GenericStatementNode( + handleStringTemplate(node.expression as SingleStringLiteral))); + } else if (node.expression is InstanceCreationExpression) { + var gnNode = NewOperatorStatementNode(); + gnNode = handleCreationCall(node.expression as InstanceCreationExpression); + func?.body.push(gnNode); + } else if (node.expression is ListLiteral) { + var gnNode = ListLiteralStatementNode(); + var currentNode = node.expression as ListLiteral; + currentNode.elements.forEach((e) => gnNode.addElement(e)); + func?.body.push(gnNode); + } else { + func?.body.push(GenericStatementNode(node.toSource())); + } + return null; +} diff --git a/dart2js/lib/src/visitor/handle/handleBreakStatement.dart b/dart2js/lib/src/visitor/handle/handleBreakStatement.dart new file mode 100644 index 00000000..952bec60 --- /dev/null +++ b/dart2js/lib/src/visitor/handle/handleBreakStatement.dart @@ -0,0 +1,8 @@ +import 'package:analyzer/dart/ast/ast.dart'; + +import '../../node/FunctionDeclarationNode.dart'; +import '../../node/GenericStatementNode.dart'; + +void handleBreakStatement(BreakStatement node, FunctionDeclarationNode? func) { + func?.body.push(GenericStatementNode(node.toString())); +} diff --git a/dart2js/lib/src/visitor/handle/handleChainIfStatement.dart b/dart2js/lib/src/visitor/handle/handleChainIfStatement.dart new file mode 100644 index 00000000..fdbab67e --- /dev/null +++ b/dart2js/lib/src/visitor/handle/handleChainIfStatement.dart @@ -0,0 +1,23 @@ +import 'package:analyzer/dart/ast/ast.dart'; + +import '../../node/IfStatementNode.dart'; +import '../../covert/convertBlock.dart'; +import '../../covert/convertExpression.dart'; + +void handleChainIfStatement(IfStatement? node, IfStatementNode? gnNode) { + gnNode?.condition = convertExpression(node?.condition.toString() ?? ''); + gnNode?.thenBody = node?.thenStatement is Block + ? convertBlock(node?.thenStatement.toString() ?? '') + : convertExpression(node?.thenStatement.toString() ?? ''); + if (node?.elseStatement != null) { + if (node?.elseStatement is IfStatement) { + gnNode?.elseBody = IfStatementNode(); + handleChainIfStatement( + node?.elseStatement as IfStatement?, gnNode?.elseBody); + } else { + gnNode?.lastElseBody = node?.elseStatement is Block + ? convertBlock(node?.elseStatement.toString() ?? '') + : convertExpression(node?.elseStatement.toString() ?? ''); + } + } +} diff --git a/dart2js/lib/src/visitor/handle/handleContinueStatement.dart b/dart2js/lib/src/visitor/handle/handleContinueStatement.dart new file mode 100644 index 00000000..8b3e0338 --- /dev/null +++ b/dart2js/lib/src/visitor/handle/handleContinueStatement.dart @@ -0,0 +1,9 @@ +import 'package:analyzer/dart/ast/ast.dart'; + +import '../../node/FunctionDeclarationNode.dart'; +import '../../node/GenericStatementNode.dart'; + +void handleContinueStatement( + ContinueStatement node, FunctionDeclarationNode? func) { + func?.body.push(GenericStatementNode(node.toString())); +} diff --git a/dart2js/lib/src/visitor/handle/handleCreationCall.dart b/dart2js/lib/src/visitor/handle/handleCreationCall.dart new file mode 100644 index 00000000..02dcaf11 --- /dev/null +++ b/dart2js/lib/src/visitor/handle/handleCreationCall.dart @@ -0,0 +1,20 @@ +import 'package:analyzer/dart/ast/ast.dart'; + +import '../../covert/convertExpression.dart'; +import '../../node/NewOperatorStatementNode.dart'; + +NewOperatorStatementNode handleCreationCall( InstanceCreationExpression currentNode) { + var gnNode = NewOperatorStatementNode(); + gnNode.methodName = currentNode.constructorName.toString(); + currentNode.argumentList.arguments.forEach((arg) { + if (arg is NamedExpression) { + gnNode.namedParameters.add([ + arg.name.label.toString(), + convertExpression(arg.expression.toString()) + ]); + } else { + gnNode.unnamedParameters.add(convertExpression(arg.toString())); + } + }); + return gnNode; +} \ No newline at end of file diff --git a/dart2js/lib/src/visitor/handle/handleDoStatement.dart b/dart2js/lib/src/visitor/handle/handleDoStatement.dart new file mode 100644 index 00000000..28ac74db --- /dev/null +++ b/dart2js/lib/src/visitor/handle/handleDoStatement.dart @@ -0,0 +1,14 @@ +import 'package:analyzer/dart/ast/ast.dart'; + +import '../../covert/convertBlock.dart'; +import '../../covert/convertExpression.dart'; +import '../../node/DoWhileStatementNode.dart'; +import '../../node/FunctionDeclarationNode.dart'; + +void handleDoStatement(DoStatement node, FunctionDeclarationNode? func) { + var gnNode = DoWhileStatementNode(); + gnNode.condition = convertExpression(node.condition.toString()); + gnNode.body = convertBlock(node.body.toString()); + func?.body.push(gnNode); + return null; +} diff --git a/dart2js/lib/src/visitor/handle/handleExpressionFunctionBody.dart b/dart2js/lib/src/visitor/handle/handleExpressionFunctionBody.dart new file mode 100644 index 00000000..d212397f --- /dev/null +++ b/dart2js/lib/src/visitor/handle/handleExpressionFunctionBody.dart @@ -0,0 +1,12 @@ +import 'package:analyzer/dart/ast/ast.dart'; + +import '../../covert/convertExpression.dart'; +import '../../node/FunctionDeclarationNode.dart'; +import '../../node/GenericStatementNode.dart'; + +void handleExpressionFunctionBody(ExpressionFunctionBody node,FunctionDeclarationNode? func){ + // print('ExpressionFunctionBody:' + node.toSource()); + func?.body.push( + GenericStatementNode(convertExpression(node.expression.toString()))); + return null; +} \ No newline at end of file diff --git a/dart2js/lib/src/visitor/handle/handleForStatement.dart b/dart2js/lib/src/visitor/handle/handleForStatement.dart new file mode 100644 index 00000000..2abf1614 --- /dev/null +++ b/dart2js/lib/src/visitor/handle/handleForStatement.dart @@ -0,0 +1,55 @@ +import 'package:analyzer/dart/ast/ast.dart'; + +import '../../covert/convertBlock.dart'; +import '../../covert/convertExpression.dart'; +import '../../covert/convertStatements.dart'; +import '../../node/ForInStatementNode.dart'; +import '../../node/ForStatementNode.dart'; +import '../../node/FunctionDeclarationNode.dart'; +import '../../node/GenericStatementNode.dart'; + +void handleForStatement(ForStatement node,FunctionDeclarationNode? func){ + var gnNode = ForStatementNode(); + if (node.forLoopParts is ForPartsWithDeclarations) { + var forLoopParts = node.forLoopParts as ForPartsWithDeclarations; + gnNode.initExpr = + convertStatements(forLoopParts.variables.toString() + ';'); + if (forLoopParts.condition != null) { + gnNode.conditionalExpr = + convertStatements(forLoopParts.condition.toString() + ';'); + } + gnNode.stepExpr = forLoopParts.updaters + .map((e) => convertExpression(e.toString())) + .join(','); + gnNode.body = node.body is Block + ? convertBlock(node.body.toString()) + : convertStatements(node.body.toString()); + func?.body.push(gnNode); + } else if (node.forLoopParts is ForPartsWithExpression) { + var forLoopParts = node.forLoopParts as ForPartsWithExpression; + if (forLoopParts.condition != null) { + gnNode.conditionalExpr = + convertStatements(forLoopParts.condition.toString() + ';'); + } + gnNode.stepExpr = forLoopParts.updaters + .map((e) => convertExpression(e.toString())) + .join(','); + gnNode.body = node.body is Block + ? convertBlock(node.body.toString()) + : convertStatements(node.body.toString()); + func?.body.push(gnNode); + } else if (node.forLoopParts is ForEachPartsWithDeclaration) { + var forLoopParts = node.forLoopParts as ForEachPartsWithDeclaration; + var gnForInNode = ForInStatementNode(); + gnForInNode.loopVariable = + forLoopParts.loopVariable.identifier.toString(); + gnForInNode.iterable = GenericStatementNode( + convertExpression(forLoopParts.iterable.toString())); + gnForInNode.body = node.body is Block + ? convertBlock(node.body.toString()) + : convertStatements(node.body.toString()); + func?.body.push(gnForInNode); + } + + return null; +} \ No newline at end of file diff --git a/dart2js/lib/src/visitor/handle/handleFuncitonExpression.dart b/dart2js/lib/src/visitor/handle/handleFuncitonExpression.dart new file mode 100644 index 00000000..19043634 --- /dev/null +++ b/dart2js/lib/src/visitor/handle/handleFuncitonExpression.dart @@ -0,0 +1,10 @@ +import 'package:analyzer/dart/ast/ast.dart'; + +import '../../covert/convertArrayFuncExpression.dart'; +import '../../covert/convertFunctionExpression.dart'; + +String handleFuncitonExpression(FunctionExpression currentNode) { + return currentNode.body is ExpressionFunctionBody + ? convertArrayFuncExpression(currentNode) + : convertFunctionExpression(currentNode.toString()); +} \ No newline at end of file diff --git a/dart2js/lib/src/visitor/handle/handleFunctionDeclarationStatement.dart b/dart2js/lib/src/visitor/handle/handleFunctionDeclarationStatement.dart new file mode 100644 index 00000000..6ed472fb --- /dev/null +++ b/dart2js/lib/src/visitor/handle/handleFunctionDeclarationStatement.dart @@ -0,0 +1,11 @@ +import 'package:analyzer/dart/ast/ast.dart'; + +import '../../covert/convertFunction.dart'; +import '../../node/FunctionDeclarationNode.dart'; +import '../../node/GenericStatementNode.dart'; + +handleFunctionDeclarationStatement( + FunctionDeclarationStatement node, FunctionDeclarationNode? func) { + func?.body.push(GenericStatementNode( + convertFunction(node.functionDeclaration.toString()))); +} diff --git a/dart2js/lib/src/visitor/handle/handleIfStatement.dart b/dart2js/lib/src/visitor/handle/handleIfStatement.dart new file mode 100644 index 00000000..958e82ce --- /dev/null +++ b/dart2js/lib/src/visitor/handle/handleIfStatement.dart @@ -0,0 +1,11 @@ +import 'package:analyzer/dart/ast/ast.dart'; + +import 'handleChainIfStatement.dart'; +import '../../node/FunctionDeclarationNode.dart'; +import '../../node/IfStatementNode.dart'; + +void handleIfStatement(IfStatement node,FunctionDeclarationNode? func){ + var gnNode = IfStatementNode(); + handleChainIfStatement(node, gnNode); + func?.body.push(gnNode); +} \ No newline at end of file diff --git a/dart2js/lib/src/visitor/handle/handleMapLiteral.dart b/dart2js/lib/src/visitor/handle/handleMapLiteral.dart new file mode 100644 index 00000000..249c0177 --- /dev/null +++ b/dart2js/lib/src/visitor/handle/handleMapLiteral.dart @@ -0,0 +1,26 @@ +import 'package:analyzer/dart/ast/ast.dart'; + +import '../../covert/convertExpression.dart'; + +String handleMapLiteral(SetOrMapLiteral literal) { + if (literal.elements.isEmpty) { + return '{}'; + } else { + if (literal.elements.first is MapLiteralEntry) { + var res = StringBuffer('{'); + literal.elements.cast().forEach((element) { + res.write( + '[${convertExpression(element.key.toString())}]: ${element.value is MapLiteralEntry ? handleMapLiteral(element.value as SetOrMapLiteral) : convertExpression(element.value.toString())},'); + }); + res.write('}'); + return res.toString(); + } else { + var res = StringBuffer('['); + literal.elements.forEach((element) { + res.write('${convertExpression(element.toString())},'); + }); + res.write(']'); + return res.toString(); + } + } +} \ No newline at end of file diff --git a/dart2js/lib/src/visitor/handle/handleMethodInvocation.dart b/dart2js/lib/src/visitor/handle/handleMethodInvocation.dart new file mode 100644 index 00000000..5ce9426e --- /dev/null +++ b/dart2js/lib/src/visitor/handle/handleMethodInvocation.dart @@ -0,0 +1,25 @@ +import 'package:analyzer/dart/ast/ast.dart'; + +import '../../covert/convertExpression.dart'; +import '../../node/MethodInvokeStatementNode.dart'; + +MethodInvokeStatementNode handleMethodInvocation( + MethodInvocation currentNode, String? parentClass) { + var gnNode = MethodInvokeStatementNode(); + gnNode.parentClassName = parentClass; + gnNode.thiz = currentNode.target != null + ? convertExpression(currentNode.target.toString()) + : ''; + gnNode.methodName = currentNode.methodName.toString(); + currentNode.argumentList.arguments.forEach((arg) { + if (arg is NamedExpression) { + gnNode.namedParameters.add([ + arg.name.label.toString(), + convertExpression(arg.expression.toString()) + ]); + } else { + gnNode.unnamedParameters.add(convertExpression(arg.toString())); + } + }); + return gnNode; +} diff --git a/dart2js/lib/src/visitor/handle/handleReturnStatement.dart b/dart2js/lib/src/visitor/handle/handleReturnStatement.dart new file mode 100644 index 00000000..1b4adf0e --- /dev/null +++ b/dart2js/lib/src/visitor/handle/handleReturnStatement.dart @@ -0,0 +1,12 @@ +import 'package:analyzer/dart/ast/ast.dart'; + +import '../../covert/convertExpression.dart'; +import '../../node/FunctionDeclarationNode.dart'; +import '../../node/ReturnStatementNode.dart'; + +handleReturnStatement(ReturnStatement node, FunctionDeclarationNode? func) { + var gnNode = ReturnStatementNode(); + gnNode.expr = convertExpression(node.expression.toString()); + func?.body.push(gnNode); + return null; +} diff --git a/dart2js/lib/src/visitor/handle/handleStringTemplate.dart b/dart2js/lib/src/visitor/handle/handleStringTemplate.dart new file mode 100644 index 00000000..5c6b8673 --- /dev/null +++ b/dart2js/lib/src/visitor/handle/handleStringTemplate.dart @@ -0,0 +1,28 @@ +import 'package:analyzer/dart/ast/ast.dart'; + +String handleStringTemplate(SingleStringLiteral node) { + var res = StringBuffer(); + var quote = ''; + if (node is StringInterpolation) { + quote = '`'; + res.write(quote); + node.elements.forEach((element) { + if (element is InterpolationString) { + res.write(element.value); + } else if (element is InterpolationExpression) { + res.write('''\${${element.expression.toString()}}'''); + } + }); + } else if (node is SimpleStringLiteral) { + var lexeme = node.literal.lexeme; + quote = + lexeme.length > 3 && lexeme.substring(0, 3) == "'''" ? '`' : lexeme[0]; + res.write(quote); + res.write(node.value); + } else { + throw 'Unsupported string literal: ${node.stringValue}'; + } + + res.write(quote); + return res.toString(); +} diff --git a/dart2js/lib/src/visitor/handle/handleSwitchStatement.dart b/dart2js/lib/src/visitor/handle/handleSwitchStatement.dart new file mode 100644 index 00000000..e5d0c467 --- /dev/null +++ b/dart2js/lib/src/visitor/handle/handleSwitchStatement.dart @@ -0,0 +1,36 @@ +import 'package:analyzer/dart/ast/ast.dart'; + +import '../../covert/convertExpression.dart'; +import '../../covert/convertStatements.dart'; +import '../../node/FunctionDeclarationNode.dart'; +import '../../node/GenericStatementNode.dart'; +import '../../node/SwitchStatementNode.dart'; + +void handleSwitchStatement(SwitchStatement node,FunctionDeclarationNode? func){ + var gnNode = SwitchStatementNode(); + gnNode.expr = + GenericStatementNode(convertExpression(node.expression.toString())); + if (node.members.isNotEmpty) { + gnNode.cases = []; + node.members.forEach((element) { + if (element is SwitchCase) { + if (element.statements.isEmpty) { + gnNode.cases?.add([element.expression.toString()]); + } else { + gnNode.cases?.add([ + element.expression.toString(), + convertStatements( + element.statements.map((e) => e.toString()).join('')) + ]); + } + } else if (element is SwitchDefault) { + gnNode.default_ = convertStatements( + element.statements.map((e) => e.toString()).join('')); + } else { + throw Exception('error: ${element.toString()}'); + } + }); + } + func?.body.push(gnNode); + return null; +} \ No newline at end of file diff --git a/dart2js/lib/src/visitor/handle/handleTryStatement.dart b/dart2js/lib/src/visitor/handle/handleTryStatement.dart new file mode 100644 index 00000000..4dc62de1 --- /dev/null +++ b/dart2js/lib/src/visitor/handle/handleTryStatement.dart @@ -0,0 +1,36 @@ +import 'package:analyzer/dart/ast/ast.dart'; + +import '../../covert/convertExpression.dart'; +import '../../node/FunctionDeclarationNode.dart'; +import '../../node/TryStatementNode.dart'; +import '../../covert/convertBlock.dart'; + +handleTryStatement(TryStatement node, FunctionDeclarationNode? func) { + // print("try1: " + node.toString()); + // print("try2: " + node.body.toString()); + + var gnNode = TryStatementNode(); + + // 处理try 中的body + gnNode.tryBody = node?.body is Block + ? convertBlock(node.body.toString() ?? '') + : convertExpression(node.body.toString()); + + // 处理catch 中的内容 + + node.catchClauses.forEach((element) { + // 暂时不支持on 语法,有 on语法的 expception 暂时不处理 + if (element.onKeyword == null) { + gnNode.catchBody = convertBlock(element.body.toString() ?? ''); + gnNode.exceptionParameter = element.exceptionParameter.toString(); + gnNode.stackTraceParameter = element.stackTraceParameter.toString(); + } + }); + + // 处理final 中的逻辑 + if (node.finallyBlock != null) { + gnNode.finallyBody = convertBlock(node.finallyBlock.toString() ?? ''); + } + + func?.body.push(gnNode); +} diff --git a/dart2js/lib/src/visitor/handle/handleVariableDeclarationStatement.dart b/dart2js/lib/src/visitor/handle/handleVariableDeclarationStatement.dart new file mode 100644 index 00000000..be9c402a --- /dev/null +++ b/dart2js/lib/src/visitor/handle/handleVariableDeclarationStatement.dart @@ -0,0 +1,39 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import '../../covert/convertExpression.dart'; +import 'handleStringTemplate.dart'; + +import '../../node/DeclarationStatmentNode.dart'; +import '../../node/FunctionDeclarationNode.dart'; +import '../../utils/const.dart'; +import 'handleFuncitonExpression.dart'; +import 'handleMapLiteral.dart'; + +void handleVariableDeclarationStatement(VariableDeclarationStatement node,FunctionDeclarationNode? func){ + // print('[var]:' + node.toSource()); + var gnNode = DeclarationStatmentNode(); + node.variables.variables.forEach((element) { + if (element.initializer is SingleStringLiteral) { + gnNode.variables.add( + '''${element.name.toString()} = ${handleStringTemplate(element.initializer as SingleStringLiteral)}'''); + } else if (element.initializer is FunctionExpression) { + var currentNode = (element.initializer as FunctionExpression); + var initializer = handleFuncitonExpression(currentNode); + gnNode.variables.add('''${element.name.toString()} = $initializer'''); + } else if (element.initializer is SetOrMapLiteral) { + gnNode.variables.add( + '''${element.name.toString()} = convertObjectLiteralToSetOrMap(${handleMapLiteral(element.initializer as SetOrMapLiteral)})'''); + } else if (element.initializer is SuperExpression) { + gnNode.variables + .add('''${element.name.toString()} = $superSubstitution'''); + } else { + if (element.initializer != null) { + gnNode.variables.add( + '''${element.name.toString()} = ${convertExpression(element.initializer.toString())}'''); + } else { + gnNode.variables.add('''${element.name.toString()}'''); + } + } + }); + func?.body.push(gnNode); + return null; +} \ No newline at end of file diff --git a/dart2js/lib/src/visitor/handle/handleWhileStatement.dart b/dart2js/lib/src/visitor/handle/handleWhileStatement.dart new file mode 100644 index 00000000..e98448f0 --- /dev/null +++ b/dart2js/lib/src/visitor/handle/handleWhileStatement.dart @@ -0,0 +1,16 @@ +import 'package:analyzer/dart/ast/ast.dart'; + +import '../../covert/convertBlock.dart'; +import '../../covert/convertExpression.dart'; +import '../../node/FunctionDeclarationNode.dart'; +import '../../node/WhileStatementNode.dart'; + +void handleWhileStatement(WhileStatement node, FunctionDeclarationNode? func) { + // print('node:' + node.toSource()); + var gnNode = WhileStatementNode(); + gnNode.condition = convertExpression(node.condition.toString()); + gnNode.body = + node.body is EmptyStatement ? '' : convertBlock(node.body.toString()); + func?.body.push(gnNode); + return null; +}