diff --git a/migrator/src/com/ohos/migrator/java/JavaTransformer.java b/migrator/src/com/ohos/migrator/java/JavaTransformer.java index 611d2c416f7df37e494f7ba55c4e1db10eed8027..f505aabf153a25de8cd71f194005437a89eb6497 100644 --- a/migrator/src/com/ohos/migrator/java/JavaTransformer.java +++ b/migrator/src/com/ohos/migrator/java/JavaTransformer.java @@ -51,6 +51,8 @@ public class JavaTransformer extends ASTVisitor implements Transformer { private final String ENUM_CONST_ORDINAL = "ENUM_CONST_ORDINAL"; + private final String METHOD_REF_PARAM_PREFIX = "__migrator_lambda_param_"; + private final ITypeBinding RUNTIME_EXCEPTION_TYPE; private final ITypeBinding THROWABLE_TYPE; @@ -1069,24 +1071,33 @@ public class JavaTransformer extends ASTVisitor implements Transformer { return false; } - private void translateType(ITypeBinding javaType) { - boolean needPrimaryType = isInPrimaryTypeContext(); - if (needPrimaryType) pushCurrent(new PrimaryTypeContext(stsCurrent, 0)); + private void translateType(ITypeBinding javaType, ASTNode javaNode) { + if (javaType == null || javaType.isRecovered()) { + // Warn and emit __UnknownType__ as result type + String message = String.format("Unrecognized type %s", javaType != null ? javaType.getName() : ""); + reportError(message, javaNode); + stsCurrent.addChild(NodeBuilder.unknownTypeAnnotation(javaType)).setParent(stsCurrent); + } + else { + // NOTE: Method "ITypeBinding.getQualifiedName()" constructs fully-qualified name + // of the corresponding type, including its generic part, array brackets, etc., + // and returns it as a string value. Using this method, we can avoid constructing + // entire STS tree for the corresponding type, simply making an identifier token + // with string value of type's fully-qualified name and wrapping it with a single + // TypeReferenceContext node. + // + // See the following link for details: + // https://help.eclipse.org/latest/topic/org.eclipse.jdt.doc.isv/reference/api/org/eclipse/jdt/core/dom/ITypeBinding.html#getQualifiedName() + pushCurrent(new TypeAnnotationContext(stsCurrent, 0)); + pushCurrent(new PrimaryTypeContext(stsCurrent, 0)); - // NOTE: Method "ITypeBinding.getQualifiedName()" constructs fully-qualified name - // of the corresponding type, including its generic part, array brackets, etc., - // and returns it as a string value. Using this method, we can avoid constructing - // entire STS tree for the corresponding type, simply making an identifier token - // with string value of type's fully-qualified name and wrapping it with a single - // TypeReferenceContext node. - // - // See the following link for details: - // https://help.eclipse.org/latest/topic/org.eclipse.jdt.doc.isv/reference/api/org/eclipse/jdt/core/dom/ITypeBinding.html#getQualifiedName() + TypeReferenceContext stsTypeRef = NodeBuilder.typeReference(javaType.getQualifiedName()); + stsCurrent.addChild(stsTypeRef).setParent(stsCurrent); - TypeReferenceContext stsTypeRef = NodeBuilder.typeReference(javaType.getQualifiedName()); - stsCurrent.addChild(stsTypeRef).setParent(stsCurrent); + popCurrent(); // PrimaryTypeContext + popCurrent(); // TypeAnnotationContext + } - if (needPrimaryType) popCurrent(); // PrimaryTypeContext } // Java tree: @@ -1544,6 +1555,9 @@ public class JavaTransformer extends ASTVisitor implements Transformer { pushCurrent(new ConstructorDeclarationContext(stsCurrent, 0)); stsCurrent.addChild(NodeBuilder.terminalNode(StaticTSParser.Constructor)).setParent(stsCurrent); + // STS: typeParameters: LessThan typeParameterList? MoreThan + createStsTypeParameters(javaMethodDeclaration.typeParameters()); + // STS: OpenParen parameterList? CloseParen createStsParameterList(javaMethodDeclaration.parameters()); } @@ -1689,9 +1703,6 @@ public class JavaTransformer extends ASTVisitor implements Transformer { return false; } - // NOTE: All Java enums are translated into STS classes because of - // built-in methods values() and valueOf() available to the former! - // // Java tree: // VariableDeclarationFragment: // Identifier { Dimension } [ = Expression ] @@ -1710,14 +1721,12 @@ public class JavaTransformer extends ASTVisitor implements Transformer { IVariableBinding variableBinding = javaVariableDeclarationFragment.resolveBinding(); if (variableBinding != null) { - pushCurrent(new TypeAnnotationContext(stsCurrent, 0)); - translateType(variableBinding.getType()); - popCurrent(); // TypeAnnotationContext + translateType(variableBinding.getType(), javaVariableDeclarationFragment); } else { // Warn and emit __UnknownType__ as variable type reportError("Failed to resolve lambda parameter", javaVariableDeclarationFragment); - stsCurrent.addChild(NodeBuilder.unknownTypeAnnotation(null)).setParent(stsCurrent); + stsCurrent.addChild(NodeBuilder.unknownTypeAnnotation()).setParent(stsCurrent); } // Note: no need to process the "{ Dimension }" part, as the extra dimensions @@ -2832,6 +2841,8 @@ public class JavaTransformer extends ASTVisitor implements Transformer { pushCurrent(new NewClassExpressionContext(pushSingleExpression())); stsCurrent.addChild(NodeBuilder.terminalNode(StaticTSParser.New)).setParent(stsCurrent); + translateTypeArguments(javaClassInstanceCreation.typeArguments()); + // Add outer class object, if any. Expression javaOuterObject = javaClassInstanceCreation.getExpression(); if (javaOuterObject != null) javaOuterObject.accept(this); @@ -3562,14 +3573,12 @@ public class JavaTransformer extends ASTVisitor implements Transformer { IMethodBinding lambdaMethod = javaLambdaExpr.resolveMethodBinding(); if (lambdaMethod != null) { - pushCurrent(new TypeAnnotationContext(stsCurrent, 0)); - translateType(lambdaMethod.getReturnType()); - popCurrent(); // TypeAnnotationContext + translateType(lambdaMethod.getReturnType(), javaLambdaExpr); } else { // Warn and emit __UnknownType__ as return type reportError("Failed to resolve lambda expression", javaLambdaExpr); - stsCurrent.addChild(NodeBuilder.unknownTypeAnnotation(null)).setParent(stsCurrent); + stsCurrent.addChild(NodeBuilder.unknownTypeAnnotation()).setParent(stsCurrent); } stsCurrent.addChild(NodeBuilder.terminalNode(StaticTSParser.Arrow)); @@ -3594,14 +3603,49 @@ public class JavaTransformer extends ASTVisitor implements Transformer { return false; } - @Override - public boolean visit(ExpressionMethodReference javaExprMethodRef) { - // TODO: To be implemented - // Emit __untranslated_expression call with commented-out original syntax as argument for now. - // This is done to avoid building invalid STS AST which causes exceptions in StaticTSWriter. - stsCurrent.addChild(NodeBuilder.untranslatedExpression(javaExprMethodRef)).setParent(stsCurrent); + private void createMethodRefLambdaParam(ITypeBinding paramType, int lambdaParamIdx, MethodReference javaMethodRef) { + pushCurrent(new ParameterContext(stsCurrent, 0)); + stsCurrent.addChild(NodeBuilder.terminalIdentifier(METHOD_REF_PARAM_PREFIX + lambdaParamIdx)); + translateType(paramType, javaMethodRef); + popCurrent(); // ParameterContext + } - return false; + private void wrapMethodRefCallWithLambda(IMethodBinding javaMethodBinding, ParserRuleContext stsMethodRefCall, + MethodReference javaMethodRef, boolean needInstanceParam) { + pushCurrent(new LambdaExpressionContext(pushSingleExpression())); + + // Create lambda parameters + ITypeBinding[] javaParamTypes = javaMethodBinding.getParameterTypes(); + if (javaParamTypes.length > 0 || needInstanceParam) { + pushCurrent(new ParameterListContext(stsCurrent, 0)); + int lambdaParamIdx = 1; + + if (needInstanceParam) { + // Insert additional parameter representing the instance + // of the referenced method. + createMethodRefLambdaParam(javaMethodBinding.getDeclaringClass(), lambdaParamIdx++, javaMethodRef); + } + + for (ITypeBinding paramType : javaParamTypes) { + createMethodRefLambdaParam(paramType, lambdaParamIdx++, javaMethodRef); + } + popCurrent(); // ParameterListContext + } + + // Create lambda return type + ITypeBinding returnType = javaMethodBinding.isConstructor() + ? javaMethodBinding.getDeclaringClass() + : javaMethodBinding.getReturnType(); + translateType(returnType, javaMethodRef); + + stsCurrent.addChild(NodeBuilder.terminalNode(StaticTSParser.Arrow)); + + // Create lambda body. It consists of the method reference call. + pushCurrent(new LambdaBodyContext(stsCurrent, 0)); + stsCurrent.addChild(stsMethodRefCall).setParent(stsCurrent); + popCurrent(); // LambdaBodyContext + + popSingleExpression(); // LambdaExpressionContext } // Java tree: @@ -3610,42 +3654,371 @@ public class JavaTransformer extends ASTVisitor implements Transformer { // [ < Type { , Type } > ] // new // STS tree: - // TODO: + // singleExpression: + // OpenParen parameterList? CloseParen typeAnnotation Arrow lambdaBody # LambdaExpression + // where + // lambdaBody: singleExpression + // singleExpression: New typeArguments? typeReference arguments # NewClassExpression @Override public boolean visit(CreationReference javaCreationRef) { - // TODO: To be implemented - // Emit __untranslated_expression call with commented-out original syntax as argument for now. - // This is done to avoid building invalid STS AST which causes exceptions in StaticTSWriter. - stsCurrent.addChild(NodeBuilder.untranslatedExpression(javaCreationRef)).setParent(stsCurrent); + if (javaCreationRef.getType().isArrayType()) + translateArrayCreationReference(javaCreationRef); + else + translateClassCreationReference(javaCreationRef); + exprTransformed.add(javaCreationRef); return false; } + private void translateArrayCreationReference(CreationReference javaCreationRef) { + // The creation reference for an array type considers a single notional + // method that performs an array creation. The method has single parameter + // of type 'int' that specifies the size of array and returns the array type. + // In case of multi-dimensional array, only first dimension will be specified. + + // Create lambda expression that wraps up the array creation expression. + pushCurrent(new LambdaExpressionContext(pushSingleExpression())); + + // Lambda has single parameter specifying the size of array. + pushCurrent(new ParameterListContext(stsCurrent, 0)); + stsCurrent.addChild(NodeBuilder.parameter(METHOD_REF_PARAM_PREFIX + "1", PrimitiveType.INT)).setParent(stsCurrent); + popCurrent(); // ParameterListContext + + // Lambda return type is an array type. + pushCurrent(new TypeAnnotationContext(stsCurrent, 0)); + ArrayType javaArrayType = (ArrayType) javaCreationRef.getType(); + javaArrayType.accept(this); + popCurrent(); // TypeAnnotationContext + + stsCurrent.addChild(NodeBuilder.terminalNode(StaticTSParser.Arrow)); + + // Lambda body consists of an array creation expression. + pushCurrent(new LambdaBodyContext(stsCurrent, 0)); + + // Create new array expression. + pushCurrent(new NewArrayExpressionContext(pushSingleExpression())); + stsCurrent.addChild(NodeBuilder.terminalNode(StaticTSParser.New)).setParent(stsCurrent); + + javaArrayType.getElementType().accept(this); + + // Specify first dimension of the new array with lambda's single parameter. + pushCurrent(new IndexExpressionContext(stsCurrent, 0)); + stsCurrent.addChild(NodeBuilder.identifierExpression(METHOD_REF_PARAM_PREFIX + "1")).setParent(stsCurrent); + popCurrent(); // IndexExpressionContext + + // Emit empty dimensions for all other dimensions if array is multi-dimensional. + for (int i = 1; i < javaArrayType.getDimensions(); i++) { + stsCurrent.addChild(NodeBuilder.terminalNode(StaticTSParser.OpenBracket)); + stsCurrent.addChild(NodeBuilder.terminalNode(StaticTSParser.CloseBracket)); + } + + popSingleExpression(); // NewArrayExpressionContext + popCurrent(); // LambdaBodyContext + popSingleExpression(); // LambdaExpressionContext + } + + private void translateClassCreationReference(CreationReference javaCreationRef) { + IMethodBinding javaCtorBinding = javaCreationRef.resolveMethodBinding(); + + // Exclude recovered bindings as well here, as information we need + // to extract from it below might not be available after recovery. + if (javaCtorBinding == null || javaCtorBinding.isRecovered()) { + // Warn and emit __untranslated_expression call with commented-out original syntax as argument. + reportError("Failed to resolve creation reference", javaCreationRef); + stsCurrent.addChild(NodeBuilder.untranslatedExpression(javaCreationRef)).setParent(stsCurrent); + return; + } + + boolean ctorCanThrow = javaCtorBinding.getExceptionTypes().length > 0; + + // Add TryExpressionContext if ctor invoked can throw + if (ctorCanThrow) { + pushCurrent(new TryExpressionContext(pushSingleExpression())); + stsCurrent.addChild(NodeBuilder.terminalNode(StaticTSParser.Try)); + } + + pushCurrent(new NewClassExpressionContext(pushSingleExpression())); + stsCurrent.addChild(NodeBuilder.terminalNode(StaticTSParser.New)).setParent(stsCurrent); + + translateTypeArguments(javaCreationRef.typeArguments()); + + javaCreationRef.getType().accept(this); + + createMethodRefCallArgs(javaCtorBinding.getParameterTypes().length, 1); + + popSingleExpression(); // NewClassExpressionContext + + if (ctorCanThrow) { + popSingleExpression(); // TryExpressionContext + } + + // The last child of current node is a SingleExpression which represents + // the new class expression. It will be the body of the lambda expression. + ParserRuleContext stsNewClassExpr = (ParserRuleContext)stsCurrent.getChild(stsCurrent.getChildCount() - 1); + stsCurrent.removeLastChild(); + + wrapMethodRefCallWithLambda(javaCtorBinding, stsNewClassExpr, javaCreationRef, false); + } + + private void createMethodRefCallArgs(int argsCount, int argIdx) { + pushCurrent(new ArgumentsContext(stsCurrent, 0)); + if (argsCount > 0) { + pushCurrent(new ExpressionSequenceContext(stsCurrent, 0)); + + for (int i = 0; i < argsCount; ++i) { + stsCurrent.addChild(NodeBuilder.identifierExpression(METHOD_REF_PARAM_PREFIX + (argIdx++))).setParent(stsCurrent); + } + + popCurrent(); // ExpressionSequenceContext + } + popCurrent(); // ArgumentsContext + } + // Java tree: // SuperMethodReference: - // [ ClassName . ] super :: [ < Type { , Type } > ] Identifier + // [ ClassName . ] super :: + // [ < Type { , Type } > ] + // Identifier // STS tree: - // TODO: lambda ? + // singleExpression: + // OpenParen parameterList? CloseParen typeAnnotation Arrow lambdaBody # LambdaExpression + // where + // lambdaBody: singleExpression + // singleExpression: singleExpression typeArguments? arguments # CallExpression @Override - public boolean visit(SuperMethodReference javaSuperMethodReference) { - // TODO: To be implemented - // Emit __untranslated_expression call with commented-out original syntax as argument for now. - // This is done to avoid building invalid STS AST which causes exceptions in StaticTSWriter. - stsCurrent.addChild(NodeBuilder.untranslatedExpression(javaSuperMethodReference)).setParent(stsCurrent); + public boolean visit(SuperMethodReference javaSuperMethodRef) { + IMethodBinding javaMethodBinding = javaSuperMethodRef.resolveMethodBinding(); + + // Exclude recovered bindings as well here, as information we need + // to extract from it below might not be available after recovery. + if (javaMethodBinding == null || javaMethodBinding.isRecovered()) { + // Warn and emit __untranslated_expression call with commented-out original syntax as argument. + reportError("Failed to resolve method reference", javaSuperMethodRef); + stsCurrent.addChild(NodeBuilder.untranslatedExpression(javaSuperMethodRef)).setParent(stsCurrent); + return false; + } + + boolean isThrowingCall = javaMethodBinding.getExceptionTypes().length > 0; + + // Add TryExpressionContext if this is a throwing call. + if (isThrowingCall) { + pushCurrent(new TryExpressionContext(pushSingleExpression())); + stsCurrent.addChild(NodeBuilder.terminalNode(StaticTSParser.Try)); + } + + pushCurrent(new CallExpressionContext(pushSingleExpression())); + + // | singleExpression Dot identifier # MemberAccessExpression + // where + // singleExpression: | (typeReference Dot)? Super # SuperExpression + pushCurrent(new MemberAccessExpressionContext(pushSingleExpression())); + pushCurrent(new SuperExpressionContext(pushSingleExpression())); + + Name javaQualifier = javaSuperMethodRef.getQualifier(); + if (javaQualifier != null) { + stsCurrent.addChild(NodeBuilder.typeReference(javaQualifier.getFullyQualifiedName())).setParent(stsCurrent); + } + + stsCurrent.addChild(NodeBuilder.terminalNode(StaticTSParser.Super)); + popSingleExpression(); // SuperExpressionContext + stsCurrent.addChild(NodeBuilder.terminalIdentifier(javaSuperMethodRef.getName())); + popSingleExpression(); // MemberAccessExpressionContext + + translateTypeArguments(javaSuperMethodRef.typeArguments()); + + createMethodRefCallArgs(javaMethodBinding.getParameterTypes().length, 1); + + popSingleExpression(); // CallExpressionContext + + if (isThrowingCall) { + popSingleExpression(); // TryExpressionContext + } + // The last child of current node is a SingleExpression which represents + // the method reference call. It will be the body of the lambda expression. + ParserRuleContext stsMethodRefCall = (ParserRuleContext)stsCurrent.getChild(stsCurrent.getChildCount() - 1); + stsCurrent.removeLastChild(); + + wrapMethodRefCallWithLambda(javaMethodBinding, stsMethodRefCall, javaSuperMethodRef, false); + + exprTransformed.add(javaSuperMethodRef); return false; } + // Java tree: + // TypeMethodReference: + // Type :: + // [ < Type { , Type } > ] + // Identifier + // STS tree: + // singleExpression: + // OpenParen parameterList? CloseParen typeAnnotation Arrow lambdaBody # LambdaExpression + // where + // lambdaBody: singleExpression + // singleExpression: singleExpression typeArguments? arguments # CallExpression @Override public boolean visit(TypeMethodReference javaTypeMethodRef) { - // TODO: To be implemented - // Emit __untranslated_expression call with commented-out original syntax as argument for now. - // This is done to avoid building invalid STS AST which causes exceptions in StaticTSWriter. - stsCurrent.addChild(NodeBuilder.untranslatedExpression(javaTypeMethodRef)).setParent(stsCurrent); + Type javaType = javaTypeMethodRef.getType(); + boolean usingFunctionalInterfaceMethod = false; + + IMethodBinding javaMethodBinding = javaTypeMethodRef.resolveMethodBinding(); + + // Exclude recovered bindings as well here, as information we need + // to extract from it below might not be available after recovery. + if (javaMethodBinding == null || javaMethodBinding.isRecovered()) { + if (javaType.isArrayType()) { + // For array type, the method binding may resolve to null, if the method + // is a synthetic method, generated by compiler (e.g. "int[]::clone"). + // In such case, use method of the functional interface that this + // method reference is implementing for the translation. + + // See following links for reference: + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=440000 + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=440344 + ITypeBinding javaInterface = javaTypeMethodRef.resolveTypeBinding(); + if (javaInterface != null) { + javaMethodBinding = javaInterface.getFunctionalInterfaceMethod(); + usingFunctionalInterfaceMethod = true; + } + } else { + // Warn and emit __untranslated_expression call with commented-out original syntax as argument. + reportError("Failed to resolve method reference", javaTypeMethodRef); + stsCurrent.addChild(NodeBuilder.untranslatedExpression(javaTypeMethodRef)).setParent(stsCurrent); + return false; + } + } + + translateClassMethodReference(javaTypeMethodRef, javaMethodBinding, javaTypeMethodRef.getName(), + javaType, true, usingFunctionalInterfaceMethod); + + exprTransformed.add(javaTypeMethodRef); return false; } + // Java tree: + // ExpressionMethodReference: + // Expression :: + // [ < Type { , Type } > ] + // Identifier + // STS tree: + // singleExpression: + // OpenParen parameterList? CloseParen typeAnnotation Arrow lambdaBody # LambdaExpression + // where + // lambdaBody: singleExpression + // singleExpression: singleExpression typeArguments? arguments # CallExpression + @Override + public boolean visit(ExpressionMethodReference javaExprMethodRef) { + IMethodBinding javaMethodBinding = javaExprMethodRef.resolveMethodBinding(); + + Expression javaExpr = javaExprMethodRef.getExpression(); + ITypeBinding exprType = javaExpr.resolveTypeBinding(); + boolean isArrayType = exprType != null && exprType.isArray(); + boolean usingFunctionalInterfaceMethod = false; + + // Exclude recovered bindings as well here, as information we need + // to extract from it below might not be available after recovery. + if (javaMethodBinding == null || javaMethodBinding.isRecovered()) { + if(isArrayType) { + // For array type, the method binding may resolve to null, if the method + // is a synthetic method, generated by compiler (e.g. "int[]::clone"). + // In such case, use method of the functional interface that this + // method reference is implementing for the translation. + + // See following links for reference: + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=440000 + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=440344 + ITypeBinding javaInterface = javaExprMethodRef.resolveTypeBinding(); + if (javaInterface != null) { + javaMethodBinding = javaInterface.getFunctionalInterfaceMethod(); + usingFunctionalInterfaceMethod = true; + } + } else { + // Warn and emit __untranslated_expression call with commented-out original syntax as argument. + reportError("Failed to resolve method reference", javaExprMethodRef); + stsCurrent.addChild(NodeBuilder.untranslatedExpression(javaExprMethodRef)).setParent(stsCurrent); + return false; + } + } + + // Figure out if the expression used with this method reference + // is a type name. + boolean isTypeMethodRef = false; + if (javaExpr instanceof Name) { + IBinding exprBinding = ((Name) javaExpr).resolveBinding(); + + if (exprBinding == null) { + reportError("Failed to resolve qualifier in method reference expression", javaExprMethodRef); + } + else if (exprBinding.getKind() == IBinding.TYPE) { + isTypeMethodRef = true; + } + } + + translateClassMethodReference(javaExprMethodRef, javaMethodBinding, javaExprMethodRef.getName(), + javaExpr, isTypeMethodRef, usingFunctionalInterfaceMethod); + + exprTransformed.add(javaExprMethodRef); + return false; + } + + private void translateClassMethodReference(MethodReference javaMethodRef, IMethodBinding javaMethodBinding, SimpleName javaName, + ASTNode javaTypeOrExpression, boolean isTypeMethodRef, boolean usingFunctionalInterfaceMethod) { + boolean isThrowingCall = javaMethodBinding.getExceptionTypes().length > 0; + + // Add TryExpressionContext if this is a throwing call. + if (isThrowingCall) { + pushCurrent(new TryExpressionContext(pushSingleExpression())); + stsCurrent.addChild(NodeBuilder.terminalNode(StaticTSParser.Try)); + } + + pushCurrent(new CallExpressionContext(pushSingleExpression())); + + boolean isStatic = (javaMethodBinding.getModifiers() & Modifier.STATIC) != 0; + boolean needInstanceParam = false; + int lambdaParamIdx = 1; + + // | singleExpression Dot identifier # MemberAccessExpression + pushCurrent(new MemberAccessExpressionContext(pushSingleExpression())); + if (isTypeMethodRef && !isStatic) { + // For type method reference to an instance method, the first parameter + // of the lambda expression is used as an instance for the method call. + // We need to manually add such parameter to lambda expression, as the + // receiver parameter is not present in the referenced method, unless + // we are using the binding of the functional interface method, which + // would include it to correspond to the signature of referenced method. + stsCurrent.addChild(NodeBuilder.identifierExpression(METHOD_REF_PARAM_PREFIX + lambdaParamIdx++)).setParent(stsCurrent); + needInstanceParam = !usingFunctionalInterfaceMethod; + } else { + javaTypeOrExpression.accept(this); + } + stsCurrent.addChild(NodeBuilder.terminalIdentifier(javaName)); + popSingleExpression(); // MemberAccessExpressionContext + + translateTypeArguments(javaMethodRef.typeArguments()); + + // In case of type method reference, the arity of the functional interface method + // would be one more than that of the referenced method (due to having a parameter + // for an instance). In this case, we skip one argument for the call, so that the + // arity of call corresponds to the arity of referenced method. + boolean skipOneParam = isTypeMethodRef && usingFunctionalInterfaceMethod; + int argsCount = javaMethodBinding.getParameterTypes().length; + createMethodRefCallArgs(skipOneParam ? argsCount - 1 : argsCount, lambdaParamIdx); + + popSingleExpression(); // CallExpressionContext + + if (isThrowingCall) { + popSingleExpression(); // TryExpressionContext + } + + // The last child of current node is a SingleExpression which represents the method reference call. + // It will be the body of the lambda expression. + ParserRuleContext stsMethodRefCall = (ParserRuleContext)stsCurrent.getChild(stsCurrent.getChildCount() - 1); + stsCurrent.removeLastChild(); + + wrapMethodRefCallWithLambda(javaMethodBinding, stsMethodRefCall, javaMethodRef, needInstanceParam); + } + // Java AST: // TryStatement: // try Block Catches @@ -3867,9 +4240,4 @@ public class JavaTransformer extends ASTVisitor implements Transformer { reportError("Unsupported Java syntax (yield statement)", javaYieldStatement); return false; } - - // The list of not yet translated Java Expressions: - // CreationReference, - // SuperMethodReference, - // TypeMethodReference } diff --git a/migrator/src/com/ohos/migrator/staticTS/NodeBuilder.java b/migrator/src/com/ohos/migrator/staticTS/NodeBuilder.java index 4c755679afcf689c5fa6069ee5e994923296b506..70d6744d499eb70f0f85f37a78eed23b16498ed0 100644 --- a/migrator/src/com/ohos/migrator/staticTS/NodeBuilder.java +++ b/migrator/src/com/ohos/migrator/staticTS/NodeBuilder.java @@ -411,8 +411,12 @@ public class NodeBuilder { return stsExpression; } + public static TypeAnnotationContext unknownTypeAnnotation() { + return typeAnnotation("__UnknownType__"); + } + public static TypeAnnotationContext unknownTypeAnnotation(Type javaType) { - TypeAnnotationContext stsTypeAnnotation = typeAnnotation("__UnknownType__"); + TypeAnnotationContext stsTypeAnnotation = unknownTypeAnnotation(); if (javaType != null) { stsTypeAnnotation.addChild(multiLineComment("/* " + javaType.toString() + " */")); @@ -421,6 +425,16 @@ public class NodeBuilder { return stsTypeAnnotation; } + public static TypeAnnotationContext unknownTypeAnnotation(ITypeBinding javaTypeBinding) { + TypeAnnotationContext stsTypeAnnotation = unknownTypeAnnotation(); + + if (javaTypeBinding != null) { + stsTypeAnnotation.addChild(multiLineComment("/* " + javaTypeBinding.getName() + " */")); + } + + return stsTypeAnnotation; + } + public static StatementContext untranslatedStatement(ASTNode node) { StatementContext stsStatement = new StatementContext(null, 0); ExpressionStatementContext stsExprStatement = new ExpressionStatementContext(stsStatement, 0); diff --git a/migrator/src/com/ohos/migrator/staticTS/parser/StaticTSParser.g4 b/migrator/src/com/ohos/migrator/staticTS/parser/StaticTSParser.g4 index f235d8720f04b9844391e05a099f6b74018c6dec..5d07e2e2d58168ad31f14cd3a4c795a081fd0e26 100644 --- a/migrator/src/com/ohos/migrator/staticTS/parser/StaticTSParser.g4 +++ b/migrator/src/com/ohos/migrator/staticTS/parser/StaticTSParser.g4 @@ -113,7 +113,7 @@ classMember ; constructorDeclaration - : Constructor OpenParen parameterList? CloseParen constructorBody + : Constructor typeParameters? OpenParen parameterList? CloseParen constructorBody ; parameterList @@ -421,7 +421,7 @@ singleExpression : OpenParen parameterList? CloseParen typeAnnotation Arrow lambdaBody # LambdaExpression | singleExpression indexExpression # ArrayAccessExpression | singleExpression Dot Identifier # MemberAccessExpression - | New (singleExpression Dot)? typeReference arguments? classBody? # NewClassExpression + | New typeArguments? (singleExpression Dot)? typeReference arguments? classBody? # NewClassExpression | New primaryType indexExpression+ (OpenBracket CloseBracket)* # NewArrayExpression | singleExpression typeArguments? arguments # CallExpression | singleExpression {this.notLineTerminator()}? PlusPlus # PostIncrementExpression diff --git a/migrator/src/com/ohos/migrator/staticTS/writer/StaticTSWriter.java b/migrator/src/com/ohos/migrator/staticTS/writer/StaticTSWriter.java index b50fd8cd6d7b9c6647d736f3ec804ea1e855f29c..87cab247bda829f812864832982c1879c13d8bea 100644 --- a/migrator/src/com/ohos/migrator/staticTS/writer/StaticTSWriter.java +++ b/migrator/src/com/ohos/migrator/staticTS/writer/StaticTSWriter.java @@ -403,12 +403,19 @@ public class StaticTSWriter extends StaticTSParserBaseVisitor { return null; } - // constructorDeclaration: Constructor '(' parameterList? ')' constructorBody + // constructorDeclaration: Constructor typeParameters? '(' parameterList? ')' constructorBody @Override public Void visitConstructorDeclaration(ConstructorDeclarationContext stsConstructorDeclaration) { doNeededIndent(); - sb.append(stsConstructorDeclaration.Constructor().getText()).append('('); + sb.append(stsConstructorDeclaration.Constructor().getText()); + + TypeParametersContext stsTypeParameters = stsConstructorDeclaration.typeParameters(); + if (stsTypeParameters != null) { + visitTypeParameters(stsTypeParameters); + } + + sb.append('('); ParameterListContext stsParameterList = stsConstructorDeclaration.parameterList(); if (stsParameterList != null) { @@ -1578,11 +1585,14 @@ public class StaticTSWriter extends StaticTSParserBaseVisitor { return null; } - // | New (singleExpression Dot)? typeReference arguments? classBody? # NewClassExpression + // | New typeArguments? (singleExpression Dot)? typeReference arguments? classBody? # NewClassExpression @Override public Void visitNewClassExpression(NewClassExpressionContext stsNewClassExpression) { sb.append(stsNewClassExpression.New().getText()).append(' '); + TypeArgumentsContext stsTypeArguments = stsNewClassExpression.typeArguments(); + if (stsTypeArguments != null) visitTypeArguments(stsTypeArguments); + SingleExpressionContext stsOuterObject = stsNewClassExpression.singleExpression(); if (stsOuterObject != null) { stsOuterObject.accept(this); diff --git a/migrator/test/java/method_references.java b/migrator/test/java/method_references.java new file mode 100644 index 0000000000000000000000000000000000000000..15b37546f763a6a4825ceeee5c39a7300c7b571c --- /dev/null +++ b/migrator/test/java/method_references.java @@ -0,0 +1,253 @@ +/* +* Copyright (c) 2022-2022 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package com.ohos.migrator.tests.java; + +class MethodReferencesExamples { + + interface Function0 { + T apply(); + } + interface Function1 { + R apply(T t); + } + interface Function2 { + R apply(T t, U u); + } + interface Consumer { + void apply(T t); + } + interface BiConsumer { + void apply(T t, U u); + } + + public T factory(Function0 f) { + return f.apply(); + } + public R factory(Function1 f, T t) { + return f.apply(t); + } + public R factory(Function2 f, T t, U u) { + return f.apply(t, u); + } + + static class Person { + public Person() {} + public Person(int age) {} + public Person(int age, String name) {} + + public void sleep(int hours) {} + } + + public static T mergeThings(T a, T b, Function2 merger) { + return merger.apply(a, b); + } + + public static String appendStrings(String a, String b) { + return a + b; + } + + public String appendStrings2(String a, String b) { + return a + b; + } + + static class A { + static class B { + public void foo() {} + public static void bar(String s) {} + } + } + + class Generic { + public void bar(T t) {} + } + + public static void classMethodReferences() { + MethodReferencesExamples myApp = new MethodReferencesExamples(); + + // Reference to static method | ContainingClass::staticMethodName + System.out.println(MethodReferencesExamples. + mergeThings("Hello ", "World!", MethodReferencesExamples::appendStrings)); + + // Reference to an instance method of a particular object | containingObject::instanceMethodName + System.out.println(MethodReferencesExamples. + mergeThings("Hello ", "World!", myApp::appendStrings2)); + + // Reference to an instance method of an arbitrary object of a particular type | ContainingType::methodName + System.out.println(MethodReferencesExamples. + mergeThings("Hello ", "World!", String::concat)); + + // Reference to a constructor | ClassName::new + Person person = myApp.factory(Person::new); + Person person2 = myApp.factory(Person::new, 25); + Person person3 = myApp.factory(Person::new, 40, "Peter"); + + // Reference with qualified type name + Consumer cons1 = A.B::foo; + Consumer cons2 = A.B::bar; + + // Reference with parametrized type name + BiConsumer generic = Generic::bar; + + // Reference with primary expressions + Function0 getLength = "sleeping"::length; + int length = getLength.apply(); + + Person[] persons = new Person[5]; + Consumer sleep = persons[2]::sleep; + sleep.apply(length); + } + + // Arrays + interface ArrayFactory { + T[] createArray(int size); + } + interface ArrayFactory2 { + T[][] createArray(int size); + } + interface ArrayFactory3 { + T[][][] createArray(int size); + } + + public static void arrayMethodReferences() { + MethodReferencesExamples myApp = new MethodReferencesExamples(); + + Function1 arrayClone = Integer[]::clone; + arrayClone.apply(new Integer[]{1, 2, 3}); + + int[] intArray = new int[]{1, 2, 3, 4, 5}; + Function0 arrayClone2 = intArray::clone; + intArray = arrayClone2.apply(); + + intArray = myApp.factory(intArray::clone); + intArray = myApp.factory(int[]::clone, intArray); + } + + public static void arrayCreationReferences() { + ArrayFactory arrayFactory1 = Integer[]::new; + Integer[] newArray = arrayFactory1.createArray(5); + + ArrayFactory2 arrayFactory2 = String[][]::new; + String[][] newArray2 = arrayFactory2.createArray(10); + + ArrayFactory3 arrayFactory3 = Double[][][]::new; + Double[][][] newArray3 = arrayFactory3.createArray(15); + + java.util.Arrays.stream(new Object[5]).toArray(String[]::new); + } + + // Varargs + interface IVarargs { + int m(String s, int[] i); + } + + interface IVarargs2 { + int m(String s, int... i); + } + + interface IVarargs3 { + int m(String s, int[]... i); + } + + static class VarArgs { + int m1(String s, int[] i) { return 5; } + int m2(String s, int... i) { return 5; } + int m3(String s, int[]... i) { return 5; } + } + + public static void methodReferencesWithVarargs() { + VarArgs obj = new VarArgs(); + + IVarargs i1m1 = obj::m1; + IVarargs2 i2m1 = obj::m1; + //IVarargs3 i3m1 = obj::m1; // Illegal + + IVarargs i1m2 = obj::m2; + IVarargs2 i2m2 = obj::m2; + //IVarargs3 i3m2 = obj::m2; // Illegal + + IVarargs i1m3 = obj::m3; + //IVarargs2 i2m3 = obj::m3; // Illegal + IVarargs3 i3m3 = obj::m3; + } + + // Inner/outer classes + interface FooMethod { + int foo(int x); + } + interface InnerFactory { + Outer.Inner create(); + } + static class OuterBase { + public int foo(int x) { return x + 5; } + } + public static class Outer extends OuterBase { + public int foo(int x) { return x - 5; } + + public void bar() { + FooMethod thisFoo = this::foo; + FooMethod superFoo = super::foo; + + // Local inner class + class LocalInner { + void foo() { + FooMethod outerThisFoo = Outer.this::foo; + FooMethod outerSuperFoo = Outer.super::foo; + } + } + } + + // Inner class + class Inner { + void foo() { + FooMethod outerThisFoo = Outer.this::foo; + FooMethod outerSuperFoo = Outer.super::foo; + } + + InnerFactory innerContext() { + return Inner::new; // Creation reference from Inner context + } + } + + public void outerContext() { + InnerFactory factory = Inner::new; // Creation reference from Outer context + Inner inner = factory.create(); + } + } + + // Exceptions and error handling + interface TryFunction0 { + T apply() throws MyException; + } + interface TryFunction1 { + R apply(T t) throws MyException; + } + class MyException extends Exception { } + static class Exceptions { + public Exceptions() throws MyException { } + public String getName() throws MyException { return "George"; } + } + + public static void methodReferencesWithExceptions() { + + try { + TryFunction0 createExceptions = Exceptions::new; + Exceptions exceptions = createExceptions.apply(); + + TryFunction1 getNameFun = Exceptions::getName; + String name = getNameFun.apply(exceptions); + } catch(MyException ex) { } + } +} diff --git a/migrator/test/java/method_references.java.sts b/migrator/test/java/method_references.java.sts new file mode 100644 index 0000000000000000000000000000000000000000..6659df0137710db49e91922bbdfe790444804e23 --- /dev/null +++ b/migrator/test/java/method_references.java.sts @@ -0,0 +1,244 @@ +/* +* Copyright (c) 2022-2022 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package com.ohos.migrator.tests.java; + +open class MethodReferencesExamples { + interface Function0 { + apply(): T ; + } + + interface Function1 { + apply(t : T): R ; + } + + interface Function2 { + apply(t : T, u : U): R ; + } + + interface Consumer { + apply(t : T): void ; + } + + interface BiConsumer { + apply(t : T, u : U): void ; + } + + public open factory(f : Function0): T { + return f.apply(); + } + public open factory(f : Function1, t : T): R { + return f.apply(t); + } + public open factory(f : Function2, t : T, u : U): R { + return f.apply(t, u); + } + static open class Person { + public constructor() { + } + + public constructor(age : int) { + } + + public constructor(age : int, name : String) { + } + + public open sleep(hours : int): void { + } + } + + public static mergeThings(a : T, b : T, merger : Function2): T { + return merger.apply(a, b); + } + public static appendStrings(a : String, b : String): String { + return a + b; + } + public open appendStrings2(a : String, b : String): String { + return a + b; + } + static open class A { + static open class B { + public open foo(): void { + } + public static bar(s : String): void { + } + } + + } + + open class Generic { + public open bar(t : T): void { + } + } + + public static classMethodReferences(): void { + let myApp : MethodReferencesExamples = new MethodReferencesExamples(); + System.out.println(MethodReferencesExamples.mergeThings("Hello ", "World!", (__migrator_lambda_param_1 : java.lang.String, __migrator_lambda_param_2 : java.lang.String): java.lang.String =>MethodReferencesExamples.appendStrings(__migrator_lambda_param_1, __migrator_lambda_param_2))); + System.out.println(MethodReferencesExamples.mergeThings("Hello ", "World!", (__migrator_lambda_param_1 : java.lang.String, __migrator_lambda_param_2 : java.lang.String): java.lang.String =>myApp.appendStrings2(__migrator_lambda_param_1, __migrator_lambda_param_2))); + System.out.println(MethodReferencesExamples.mergeThings("Hello ", "World!", (__migrator_lambda_param_1 : java.lang.String, __migrator_lambda_param_2 : java.lang.String): java.lang.String =>__migrator_lambda_param_1.concat(__migrator_lambda_param_2))); + let person : Person = myApp.factory((): com.ohos.migrator.tests.java.MethodReferencesExamples.Person =>new Person()); + let person2 : Person = myApp.factory((__migrator_lambda_param_1 : int): com.ohos.migrator.tests.java.MethodReferencesExamples.Person =>new Person(__migrator_lambda_param_1), 25); + let person3 : Person = myApp.factory((__migrator_lambda_param_1 : int, __migrator_lambda_param_2 : java.lang.String): com.ohos.migrator.tests.java.MethodReferencesExamples.Person =>new Person(__migrator_lambda_param_1, __migrator_lambda_param_2), 40, "Peter"); + let cons1 : Consumer = (__migrator_lambda_param_1 : com.ohos.migrator.tests.java.MethodReferencesExamples.A.B): void =>__migrator_lambda_param_1.foo(); + let cons2 : Consumer = (__migrator_lambda_param_1 : java.lang.String): void =>A.B.bar(__migrator_lambda_param_1); + let generic : BiConsumer = (__migrator_lambda_param_1 : com.ohos.migrator.tests.java.MethodReferencesExamples.Generic, __migrator_lambda_param_2 : java.lang.Integer): void =>__migrator_lambda_param_1.bar(__migrator_lambda_param_2); + let getLength : Function0 = (): int =>"sleeping".length(); + let length : int = getLength.apply(); + let persons : Person[] = new Person[5]; + let sleep : Consumer = (__migrator_lambda_param_1 : int): void =>persons[2].sleep(__migrator_lambda_param_1); + sleep.apply(length); + } + interface ArrayFactory { + createArray(size : int): T[] ; + } + + interface ArrayFactory2 { + createArray(size : int): T[][] ; + } + + interface ArrayFactory3 { + createArray(size : int): T[][][] ; + } + + public static arrayMethodReferences(): void { + let myApp : MethodReferencesExamples = new MethodReferencesExamples(); + let arrayClone : Function1 = (__migrator_lambda_param_1 : java.lang.Integer[]): java.lang.Integer[] =>__migrator_lambda_param_1.clone(); + arrayClone.apply([1, 2, 3]); + let intArray : int[] = [1, 2, 3, 4, 5]; + let arrayClone2 : Function0 = (): int[] =>intArray.clone(); + intArray = arrayClone2.apply(); + intArray = myApp.factory((): int[] =>intArray.clone()); + intArray = myApp.factory((__migrator_lambda_param_1 : int[]): int[] =>__migrator_lambda_param_1.clone(), intArray); + } + public static arrayCreationReferences(): void { + let arrayFactory1 : ArrayFactory = (__migrator_lambda_param_1 : int): Integer[] =>new Integer[__migrator_lambda_param_1]; + let newArray : Integer[] = arrayFactory1.createArray(5); + let arrayFactory2 : ArrayFactory2 = (__migrator_lambda_param_1 : int): String[][] =>new String[__migrator_lambda_param_1][]; + let newArray2 : String[][] = arrayFactory2.createArray(10); + let arrayFactory3 : ArrayFactory3 = (__migrator_lambda_param_1 : int): Double[][][] =>new Double[__migrator_lambda_param_1][][]; + let newArray3 : Double[][][] = arrayFactory3.createArray(15); + java.util.Arrays.stream(new Object[5]).toArray((__migrator_lambda_param_1 : int): String[] =>new String[__migrator_lambda_param_1]); + } + interface IVarargs { + m(s : String, i : int[]): int ; + } + + interface IVarargs2 { + m(s : String, ... i: int ): int ; + } + + interface IVarargs3 { + m(s : String, ... i: int[] ): int ; + } + + static open class VarArgs { + open m1(s : String, i : int[]): int { + return 5; + } + open m2(s : String, ... i: int ): int { + return 5; + } + open m3(s : String, ... i: int[] ): int { + return 5; + } + } + + public static methodReferencesWithVarargs(): void { + let obj : VarArgs = new VarArgs(); + let i1m1 : IVarargs = (__migrator_lambda_param_1 : java.lang.String, __migrator_lambda_param_2 : int[]): int =>obj.m1(__migrator_lambda_param_1, __migrator_lambda_param_2); + let i2m1 : IVarargs2 = (__migrator_lambda_param_1 : java.lang.String, __migrator_lambda_param_2 : int[]): int =>obj.m1(__migrator_lambda_param_1, __migrator_lambda_param_2); + let i1m2 : IVarargs = (__migrator_lambda_param_1 : java.lang.String, __migrator_lambda_param_2 : int[]): int =>obj.m2(__migrator_lambda_param_1, __migrator_lambda_param_2); + let i2m2 : IVarargs2 = (__migrator_lambda_param_1 : java.lang.String, __migrator_lambda_param_2 : int[]): int =>obj.m2(__migrator_lambda_param_1, __migrator_lambda_param_2); + let i1m3 : IVarargs = (__migrator_lambda_param_1 : java.lang.String, __migrator_lambda_param_2 : int[][]): int =>obj.m3(__migrator_lambda_param_1, __migrator_lambda_param_2); + let i3m3 : IVarargs3 = (__migrator_lambda_param_1 : java.lang.String, __migrator_lambda_param_2 : int[][]): int =>obj.m3(__migrator_lambda_param_1, __migrator_lambda_param_2); + } + interface FooMethod { + foo(x : int): int ; + } + + interface InnerFactory { + create(): Outer.Inner ; + } + + static open class OuterBase { + public open foo(x : int): int { + return x + 5; + } + } + + public static open class Outer extends OuterBase { + public override foo(x : int): int { + return x - 5; + } + public open bar(): void { + let thisFoo : FooMethod = (__migrator_lambda_param_1 : int): int =>this.foo(__migrator_lambda_param_1); + let superFoo : FooMethod = (__migrator_lambda_param_1 : int): int =>super.foo(__migrator_lambda_param_1); + open class LocalInner { + open foo(): void { + let outerThisFoo : FooMethod = (__migrator_lambda_param_1 : int): int =>Outer.this.foo(__migrator_lambda_param_1); + let outerSuperFoo : FooMethod = (__migrator_lambda_param_1 : int): int =>Outer.super.foo(__migrator_lambda_param_1); + } + } + + } + open class Inner { + open foo(): void { + let outerThisFoo : FooMethod = (__migrator_lambda_param_1 : int): int =>Outer.this.foo(__migrator_lambda_param_1); + let outerSuperFoo : FooMethod = (__migrator_lambda_param_1 : int): int =>Outer.super.foo(__migrator_lambda_param_1); + } + open innerContext(): InnerFactory { + return (): com.ohos.migrator.tests.java.MethodReferencesExamples.Outer.Inner =>new Inner(); + } + } + + public open outerContext(): void { + let factory : InnerFactory = (): com.ohos.migrator.tests.java.MethodReferencesExamples.Outer.Inner =>new Inner(); + let inner : Inner = factory.create(); + } + } + + interface TryFunction0 { + apply(): T ; + } + + interface TryFunction1 { + apply(t : T): R ; + } + + open class MyException extends Exception { + } + + static open class Exceptions { + public constructor() { + } + + public open getName(): String { + return "George"; + } + } + + public static methodReferencesWithExceptions(): void { + trap { + let createExceptions : TryFunction0 = (): com.ohos.migrator.tests.java.MethodReferencesExamples.Exceptions =>try new Exceptions(); + let exceptions : Exceptions = try createExceptions.apply(); + let getNameFun : TryFunction1 = (__migrator_lambda_param_1 : com.ohos.migrator.tests.java.MethodReferencesExamples.Exceptions): java.lang.String =>try __migrator_lambda_param_1.getName(); + let name : String = try getNameFun.apply(exceptions); + } + catch (ex : MyException) { + } + + } +} + \ No newline at end of file