| /******************************************************************************* |
| * Copyright (c) 2000, 2010 IBM Corporation and others. |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Dmitry Stalnov (dstalnov@fusionone.com) - contributed fix for |
| * o bug "inline method - doesn't handle implicit cast" (see |
| * https://bugs.eclipse.org/bugs/show_bug.cgi?id=24941). |
| * o inline call that is used in a field initializer |
| * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38137) |
| * o inline call a field initializer: could detect self reference |
| * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=44417) |
| *******************************************************************************/ |
| package org.eclipse.dltk.internal.javascript.corext.refactoring.code; |
| |
| import java.util.List; |
| |
| import org.eclipse.dltk.core.IMethod; |
| import org.eclipse.dltk.core.ISourceModule; |
| import org.eclipse.dltk.core.ModelException; |
| import org.eclipse.dltk.javascript.core.dom.DomPackage; |
| import org.eclipse.dltk.javascript.core.dom.FunctionExpression; |
| import org.eclipse.dltk.javascript.core.dom.IfStatement; |
| import org.eclipse.dltk.javascript.core.dom.IterationStatement; |
| import org.eclipse.dltk.javascript.core.dom.LabeledStatement; |
| import org.eclipse.dltk.javascript.core.dom.Node; |
| import org.eclipse.dltk.javascript.core.dom.ReturnStatement; |
| import org.eclipse.dltk.javascript.core.dom.Statement; |
| import org.eclipse.emf.common.util.TreeIterator; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.ltk.core.refactoring.RefactoringStatus; |
| |
| |
| /** |
| * A SourceProvider encapsulates a piece of code (source) and the logic to |
| * inline it into given CallContexts. |
| */ |
| public class SourceProvider { |
| |
| //private ISourceModule fTypeRoot; |
| //private IDocument fDocument; |
| private FunctionExpression declaration; |
| private SourceAnalyzer analyzer; |
| private String code; |
| private boolean mustEvalReturnedExpression; |
| /*private boolean fReturnValueNeedsLocalVariable; |
| private List fReturnExpressions; |
| private IDocument fSource; |
| |
| private static final int EXPRESSION_MODE = 1; |
| private static final int STATEMENT_MODE = 2; |
| private static final int RETURN_STATEMENT_MODE = 3; |
| private int fMarkerMode;*/ |
| |
| public SourceProvider(FunctionExpression declaration, IMethod method) { |
| //fTypeRoot = typeRoot; |
| this.declaration = declaration; |
| /*List parameters = fDeclaration.parameters(); |
| for (Iterator iter = parameters.iterator(); iter.hasNext();) { |
| SingleVariableDeclaration element = (SingleVariableDeclaration) iter |
| .next(); |
| ParameterData data = new ParameterData(element); |
| element.setProperty(ParameterData.PROPERTY, data); |
| }*/ |
| analyzer = new SourceAnalyzer(declaration, method); |
| //fReturnValueNeedsLocalVariable = true; |
| //fReturnExpressions = new ArrayList(); |
| } |
| |
| /* |
| * TODO: unit's source does not match contents of source document and |
| * declaration node. |
| * |
| * @param typeRoot |
| * the type root |
| * @param source |
| * document contining the content of the type root |
| * @param declaration |
| * |
| public SourceProvider(ITypeRoot typeRoot, IDocument source, |
| MethodDeclaration declaration) { |
| this(typeRoot, declaration); |
| fSource = source; |
| }*/ |
| |
| public RefactoringStatus checkActivation() { |
| return analyzer.checkActivation(); |
| } |
| |
| public void initialize() throws ModelException { |
| //fDocument = fSource == null ? new Document(fTypeRoot.getBuffer() |
| // .getContents()) : fSource; |
| analyzer.initialize(); |
| code = analyzer.getCode(); |
| List<Statement> list = declaration.getBody().getStatements(); |
| if (list.isEmpty()) |
| return; |
| Node last = list.get(list.size()-1); |
| if (last.eClass().getClassifierID() == DomPackage.RETURN_STATEMENT) |
| analyzeReturn(last); |
| } |
| |
| private void analyzeReturn(Node ret) { |
| TreeIterator<EObject> it = ret.eAllContents(); |
| while(it.hasNext()) { |
| Node node = (Node)it.next(); |
| switch(node.eClass().getClassifierID()) { |
| case DomPackage.CALL_EXPRESSION: |
| case DomPackage.NEW_EXPRESSION: |
| mustEvalReturnedExpression = true; |
| return; |
| } |
| } |
| } |
| |
| public boolean isExecutionFlowInterrupted() { |
| return analyzer.isExecutionFlowInterrupted(); |
| } |
| |
| /*static class VariableReferenceFinder extends ASTVisitor { |
| private boolean fResult; |
| private IVariableBinding fBinding; |
| |
| public VariableReferenceFinder(IVariableBinding binding) { |
| fBinding = binding; |
| } |
| |
| public boolean getResult() { |
| return fResult; |
| } |
| |
| public boolean visit(SimpleName node) { |
| if (!fResult) { |
| fResult = Bindings.equals(fBinding, node.resolveBinding()); |
| } |
| return false; |
| } |
| } |
| |
| /** |
| * Checks whether variable is referenced by the method declaration or not. |
| * |
| * @param binding |
| * binding of variable to check. |
| * @return <code>true</code> if variable is referenced by the method, |
| * otherwise <code>false</code> |
| * |
| public boolean isVariableReferenced(IVariableBinding binding) { |
| VariableReferenceFinder finder = new VariableReferenceFinder(binding); |
| fDeclaration.accept(finder); |
| return finder.getResult(); |
| } |
| |
| public boolean hasReturnValue() { |
| IMethodBinding binding = fDeclaration.resolveBinding(); |
| return binding.getReturnType() != fDeclaration.getAST() |
| .resolveWellKnownType("void"); //$NON-NLS-1$ |
| } |
| |
| // returns true if the declaration has a vararg and |
| // the body contains an array access to the vararg |
| public boolean hasArrayAccess() { |
| return fAnalyzer.hasArrayAccess(); |
| } |
| |
| public boolean hasSuperMethodInvocation() { |
| return fAnalyzer.hasSuperMethodInvocation(); |
| }*/ |
| |
| public boolean mustEvaluateReturnedExpression() { |
| return mustEvalReturnedExpression; |
| } |
| |
| /*public boolean returnValueNeedsLocalVariable() { |
| return fReturnValueNeedsLocalVariable; |
| } |
| |
| public int getNumberOfStatements() { |
| return fDeclaration.getBody().statements().size(); |
| }*/ |
| |
| public boolean isSimpleFunction() { |
| List<Statement> statements = declaration.getBody().getStatements(); |
| if (statements.size() != 1) |
| return false; |
| return statements.get(0) instanceof ReturnStatement; |
| } |
| |
| /*public boolean isLastStatementReturn() { |
| List statements = fDeclaration.getBody().statements(); |
| if (statements.size() == 0) |
| return false; |
| return statements.get(statements.size() - 1) instanceof ReturnStatement; |
| }*/ |
| |
| public boolean isDanglingIf() { |
| List<Statement> statements = declaration.getBody().getStatements(); |
| if (statements.size() != 1) |
| return false; |
| |
| Statement last = (Statement)statements.get(0); |
| |
| while (true) { |
| switch(last.eClass().getClassifierID()) { |
| case DomPackage.IF_STATEMENT: |
| return ((IfStatement)last).getAlternative() == null; |
| case DomPackage.BLOCK_STATEMENT: |
| return false; |
| case DomPackage.WHILE_STATEMENT: |
| case DomPackage.FOR_STATEMENT: |
| case DomPackage.FOR_IN_STATEMENT: |
| case DomPackage.FOR_EACH_IN_STATEMENT: |
| case DomPackage.DO_STATEMENT: |
| last = ((IterationStatement)last).getBody(); |
| break; |
| case DomPackage.LABELED_STATEMENT: |
| last = ((LabeledStatement)last).getStatement(); |
| break; |
| default: |
| return false; |
| } |
| } |
| } |
| |
| public FunctionExpression getDeclaration() { |
| return declaration; |
| } |
| |
| /*public String getMethodName() { |
| return fDeclaration.getName().getIdentifier(); |
| } |
| |
| public ITypeBinding getReturnType() { |
| return fDeclaration.resolveBinding().getReturnType(); |
| } |
| |
| public List getReturnExpressions() { |
| return fReturnExpressions; |
| } |
| |
| public boolean returnTypeMatchesReturnExpressions() { |
| ITypeBinding returnType = getReturnType(); |
| for (Iterator iter = fReturnExpressions.iterator(); iter.hasNext();) { |
| Expression expression = (Expression) iter.next(); |
| if (!Bindings.equals(returnType, expression.resolveTypeBinding())) |
| return false; |
| } |
| return true; |
| }*/ |
| |
| public ParameterData getParameterData(int index) { |
| return analyzer.getParameterData(index); |
| /*SingleVariableDeclaration decl = (SingleVariableDeclaration) fDeclaration |
| .parameters().get(index); |
| return (ParameterData) decl.getProperty(ParameterData.PROPERTY);*/ |
| } |
| |
| /*public ITypeRoot getTypeRoot() { |
| return fTypeRoot; |
| }*/ |
| |
| /*public boolean needsReturnedExpressionParenthesis() { |
| ASTNode last = getLastStatement(); |
| if (last instanceof ReturnStatement) { |
| return ASTNodes.needsParentheses(((ReturnStatement) last) |
| .getExpression()); |
| } |
| return false; |
| }*/ |
| |
| /*public boolean returnsConditionalExpression() { |
| ASTNode last = getLastStatement(); |
| if (last instanceof ReturnStatement) { |
| return ((ReturnStatement) last).getExpression() instanceof ConditionalExpression; |
| } |
| return false; |
| }*/ |
| |
| public int getReceiversToBeUpdated() { |
| return analyzer.getImplicitReceiversCount(); |
| } |
| |
| /*public boolean isVarargs() { |
| return fDeclaration.isVarargs(); |
| } |
| |
| public int getVarargIndex() { |
| return fDeclaration.parameters().size() - 1; |
| } |
| |
| public TextEdit getDeleteEdit() { |
| final ASTRewrite rewriter = ASTRewrite.create(fDeclaration.getAST()); |
| rewriter.remove(fDeclaration, null); |
| return rewriter.rewriteAST(fDocument, fTypeRoot.getJavaProject() |
| .getOptions(true)); |
| }*/ |
| |
| public String getCode() { |
| return code; |
| } |
| |
| private Statement getLastStatement() { |
| List<Statement> statements = declaration.getBody().getStatements(); |
| if (statements.isEmpty()) |
| return null; |
| return statements.get(statements.size() - 1); |
| } |
| |
| /*private List getReturnStatementRanges() { |
| fMarkerMode = RETURN_STATEMENT_MODE; |
| List result = new ArrayList(1); |
| List statements = fDeclaration.getBody().statements(); |
| int size = statements.size(); |
| if (size <= 1) |
| return result; |
| result.add(createRange(statements, size - 2)); |
| return result; |
| } |
| |
| private List getStatementRanges() { |
| fMarkerMode = STATEMENT_MODE; |
| List result = new ArrayList(1); |
| List statements = fDeclaration.getBody().statements(); |
| int size = statements.size(); |
| if (size == 0) |
| return result; |
| result.add(createRange(statements, size - 1)); |
| return result; |
| } |
| |
| private List getExpressionRanges() { |
| fMarkerMode = EXPRESSION_MODE; |
| List result = new ArrayList(2); |
| List statements = fDeclaration.getBody().statements(); |
| ReturnStatement rs = null; |
| int size = statements.size(); |
| ASTNode node; |
| switch (size) { |
| case 0: |
| return result; |
| case 1: |
| node = (ASTNode) statements.get(0); |
| if (node.getNodeType() == ASTNode.RETURN_STATEMENT) { |
| rs = (ReturnStatement) node; |
| } else { |
| result.add(createRange(node, node)); |
| } |
| break; |
| default: { |
| node = (ASTNode) statements.get(size - 1); |
| if (node.getNodeType() == ASTNode.RETURN_STATEMENT) { |
| result.add(createRange(statements, size - 2)); |
| rs = (ReturnStatement) node; |
| } else { |
| result.add(createRange(statements, size - 1)); |
| } |
| break; |
| } |
| } |
| if (rs != null) { |
| Expression exp = rs.getExpression(); |
| result.add(createRange(exp, exp)); |
| } |
| return result; |
| } |
| |
| private IRegion createRange(List statements, int end) { |
| ASTNode first = (ASTNode) statements.get(0); |
| ASTNode last = (ASTNode) statements.get(end); |
| return createRange(first, last); |
| } |
| |
| private IRegion createRange(ASTNode first, ASTNode last) { |
| ASTNode root = first.getRoot(); |
| if (root instanceof CompilationUnit) { |
| CompilationUnit unit = (CompilationUnit) root; |
| int start = unit.getExtendedStartPosition(first); |
| int length = unit.getExtendedStartPosition(last) - start |
| + unit.getExtendedLength(last); |
| IRegion range = new Region(start, length); |
| return range; |
| } else { |
| int start = first.getStartPosition(); |
| int length = last.getStartPosition() - start + last.getLength(); |
| IRegion range = new Region(start, length); |
| return range; |
| } |
| } |
| |
| private String[] getBlocks(RangeMarker[] markers) |
| throws BadLocationException { |
| String[] result = new String[markers.length]; |
| for (int i = 0; i < markers.length; i++) { |
| RangeMarker marker = markers[i]; |
| String content = fDocument.get(marker.getOffset(), |
| marker.getLength()); |
| String lines[] = Strings.convertIntoLines(content); |
| Strings.trimIndentation(lines, fTypeRoot.getJavaProject(), false); |
| if (fMarkerMode == STATEMENT_MODE && lines.length == 2 |
| && isSingleControlStatementWithoutBlock()) { |
| lines[1] = CodeFormatterUtil.createIndentString(1, |
| fTypeRoot.getJavaProject()) |
| + lines[1]; |
| } |
| result[i] = Strings.concatenate(lines, |
| TextUtilities.getDefaultLineDelimiter(fDocument)); |
| } |
| return result; |
| } |
| |
| private boolean isSingleControlStatementWithoutBlock() { |
| List statements = fDeclaration.getBody().statements(); |
| int size = statements.size(); |
| if (size != 1) |
| return false; |
| Statement statement = (Statement) statements.get(size - 1); |
| int nodeType = statement.getNodeType(); |
| if (nodeType == ASTNode.IF_STATEMENT) { |
| IfStatement ifStatement = (IfStatement) statement; |
| return !(ifStatement.getThenStatement() instanceof Block) |
| && !(ifStatement.getElseStatement() instanceof Block); |
| } else if (nodeType == ASTNode.FOR_STATEMENT) { |
| return !(((ForStatement) statement).getBody() instanceof Block); |
| } else if (nodeType == ASTNode.WHILE_STATEMENT) { |
| return !(((WhileStatement) statement).getBody() instanceof Block); |
| } |
| return false; |
| }*/ |
| |
| public ISourceModule getSourceModule() { |
| return analyzer.getSourceModule(); |
| } |
| } |