Package org.eclipse.php.internal.core.typeinference

Source Code of org.eclipse.php.internal.core.typeinference.BindingUtility$SourceRange

/*******************************************************************************
* Copyright (c) 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     IBM Corporation - initial API and implementation
*     Zend Technologies
*******************************************************************************/
package org.eclipse.php.internal.core.typeinference;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.dltk.ast.ASTNode;
import org.eclipse.dltk.ast.ASTVisitor;
import org.eclipse.dltk.ast.declarations.MethodDeclaration;
import org.eclipse.dltk.ast.declarations.ModuleDeclaration;
import org.eclipse.dltk.ast.expressions.Expression;
import org.eclipse.dltk.ast.references.VariableReference;
import org.eclipse.dltk.core.*;
import org.eclipse.dltk.internal.core.SourceRefElement;
import org.eclipse.dltk.ti.IContext;
import org.eclipse.dltk.ti.ISourceModuleContext;
import org.eclipse.dltk.ti.goals.ExpressionTypeGoal;
import org.eclipse.dltk.ti.types.IEvaluatedType;
import org.eclipse.php.internal.core.compiler.ast.nodes.ReturnStatement;
import org.eclipse.php.internal.core.compiler.ast.nodes.YieldExpression;
import org.eclipse.php.internal.core.typeinference.context.IModelCacheContext;
import org.eclipse.php.internal.core.typeinference.context.MethodContext;
import org.eclipse.php.internal.core.typeinference.evaluators.VariableReferenceEvaluator;

/**
* This utility allows to determine types of expressions represented in AST
* tree. Results are cached until instance of this class is alive.
*/
public class BindingUtility {

  /** Default time limit for type inference evaluation */
  private static final int TIME_LIMIT = 1000;

  private ISourceModule sourceModule;
  private ASTNode rootNode;
  private Map<SourceRange, IEvaluatedType> evaluatedTypesCache = new HashMap<SourceRange, IEvaluatedType>();
  private int timeLimit = TIME_LIMIT;
  private IModelAccessCache modelAccessCache;
  private IPHPTypeInferencer cachedInferencer = new PHPTypeInferencer();

  /**
   * Creates new instance of binding utility.
   *
   * @param sourceModule
   *            Source module of the file.
   */
  public BindingUtility(ISourceModule sourceModule) {
    this.sourceModule = sourceModule;
    this.rootNode = SourceParserUtil.getModuleDeclaration(sourceModule);
  }

  /**
   * Creates new instance of binding utility
   *
   * @param sourceModule
   *            Source module of the file.
   * @param rootNode
   *            AST tree of the the file represented by the given source
   *            module.
   */
  public BindingUtility(ISourceModule sourceModule, ASTNode rootNode) {
    this.sourceModule = sourceModule;
    this.rootNode = rootNode;
  }

  public BindingUtility(ISourceModule sourceModule,
      IModelAccessCache modelAccessCache) {
    this(sourceModule);
    this.modelAccessCache = modelAccessCache;
  }

  /**
   * Sets new time limit in milliseconds for the type inference evaluation.
   * Default value is 500ms.
   *
   * @param timeLimit
   */
  public void setTimeLimit(int timeLimit) {
    this.timeLimit = timeLimit;
  }

  /**
   * This method returns evaluated type for the given AST expression node.
   * Returns cached type from previous evaluations (if exists).
   *
   * @param node
   *            AST node that needs to be evaluated.
   * @return evaluated type.
   * @throws ModelException
   *
   * @throws IllegalArgumentException
   *             in case if context cannot be found for the given node.
   * @throws NullPointerException
   *             if the given node is <code>null</code>.
   */
  public IEvaluatedType getType(ASTNode node) throws ModelException {
    if (node == null) {
      throw new NullPointerException();
    }
    return getType(new SourceRange(node));
  }

  /**
   * This method returns evaluated type for the given model element. Returns
   * cached type from previous evaluations (if exists).
   *
   * @param element
   *            Model element.
   * @return evaluated type.
   *
   * @throws IllegalArgumentException
   *             in case if context cannot be found for the given node.
   * @throws NullPointerException
   *             if the given element is <code>null</code>.
   * @throws ModelException
   */
  public IEvaluatedType getType(SourceRefElement element)
      throws ModelException {
    if (element == null) {
      throw new NullPointerException();
    }
    ISourceModule elementModule = element.getSourceModule();
    if (!elementModule.equals(sourceModule)) {
      throw new IllegalArgumentException("Unknown model element"); //$NON-NLS-1$
    }
    return getType(new SourceRange(element.getSourceRange()));
  }

  /**
   * This method returns evaluated type for the expression under the given
   * offset and length. Returns cached type from previous evaluations (if
   * exists).
   *
   * @param startOffset
   *            Starting offset of the expression.
   * @param length
   *            Length of the expression.
   * @return evaluated type.
   *
   * @throws IllegalArgumentException
   *             in case if context cannot be found for the given node.
   */
  public IEvaluatedType getType(int startOffset, int length)
      throws ModelException {
    return getType(new SourceRange(startOffset, length));
  }

  protected IEvaluatedType getType(SourceRange sourceRange, IContext context,
      ASTNode node) {
    IPHPTypeInferencer typeInferencer = cachedInferencer == null ? new PHPTypeInferencer()
        : cachedInferencer;
    return typeInferencer.evaluateType(
        new ExpressionTypeGoal(context, node), timeLimit);
  }

  protected ContextFinder getContext(SourceRange sourceRange)
      throws ModelException {
    ContextFinder contextFinder = new ContextFinder(sourceRange);
    try {
      rootNode.traverse(contextFinder);
    } catch (Exception e) {
      throw new ModelException(e, IStatus.ERROR);
    }
    if (contextFinder.getNode() == null) {
      throw new ModelException(new IllegalArgumentException(
          "AST node can not be found for the given source range: " //$NON-NLS-1$
              + sourceRange), IStatus.ERROR);
    }
    return contextFinder;
  }

  protected IEvaluatedType getType(SourceRange sourceRange)
      throws ModelException {
    if (!evaluatedTypesCache.containsKey(sourceRange)) {
      ContextFinder contextFinder = getContext(sourceRange);
      evaluatedTypesCache.put(
          sourceRange,
          getType(sourceRange, contextFinder.getContext(),
              contextFinder.getNode()));
    }
    return evaluatedTypesCache.get(sourceRange);
  }

  /**
   * This method returns model elements for the given AST expression node.
   * This method uses cached evaluated type from previous evaluations (if
   * exists).
   *
   * @param node
   *            AST node that needs to be evaluated.
   * @return model element or <code>null</code> in case it couldn't be found
   * @throws ModelException
   *
   * @throws IllegalArgumentException
   *             in case if context cannot be found for the given node.
   * @throws NullPointerException
   *             if the given node is <code>null</code>.
   */
  public IModelElement[] getModelElement(ASTNode node) throws ModelException {
    if (node == null) {
      throw new NullPointerException();
    }
    return getModelElement(new SourceRange(node), true);
  }

  /**
   * This method returns model elements for the given model element. This
   * method uses cached evaluated type from previous evaluations (if exists).
   *
   * @param element
   *            Source Reference Model element.
   * @return model element or <code>null</code> in case it couldn't be found
   *
   * @throws IllegalArgumentException
   *             in case if context cannot be found for the given node.
   * @throws NullPointerException
   *             if the given element is <code>null</code>.
   * @throws ModelException
   */
  public IModelElement[] getModelElement(SourceRefElement element)
      throws ModelException {
    if (element == null) {
      throw new NullPointerException();
    }
    ISourceModule elementModule = element.getSourceModule();
    if (!elementModule.equals(sourceModule)) {
      throw new IllegalArgumentException("Unknown model element"); //$NON-NLS-1$
    }
    return getModelElement(new SourceRange(element.getSourceRange()), true);
  }

  /**
   * This method returns model elements for the expression under the given
   * offset and length, and will filter the results using the file-network.
   * This method uses cached evaluated type from previous evaluations (if
   * exists).
   *
   * @param startOffset
   *            Starting offset of the expression.
   * @param length
   *            Length of the expression.
   * @return model element or <code>null</code> in case it couldn't be found
   * @throws ModelException
   *
   * @throws IllegalArgumentException
   *             in case if context cannot be found for the given node.
   * @see #getModelElement(int, int, boolean)
   */
  public IModelElement[] getModelElement(int startOffset, int length)
      throws ModelException {
    return getModelElement(startOffset, length, true);
  }

  /**
   * This method returns model elements for the expression under the given
   * offset and length, and will filter the results using the file-network.
   * This method uses cached evaluated type from previous evaluations (if
   * exists).
   *
   * @param startOffset
   *            Starting offset of the expression.
   * @param length
   *            Length of the expression.
   * @param cache
   *            Model access cache if available
   * @return model element or <code>null</code> in case it couldn't be found
   * @throws ModelException
   *
   * @throws IllegalArgumentException
   *             in case if context cannot be found for the given node.
   * @see #getModelElement(int, int, boolean)
   */
  public IModelElement[] getModelElement(int startOffset, int length,
      IModelAccessCache cache) throws ModelException {
    return getModelElement(startOffset, length, true, cache);
  }

  /**
   * This method returns model elements for the expression under the given
   * offset and length. This method uses cached evaluated type from previous
   * evaluations (if exists).
   *
   * @param startOffset
   *            Starting offset of the expression.
   * @param length
   *            Length of the expression.
   * @param filter
   *            Filter the results using the 'File-Network'.
   * @return model element or <code>null</code> in case it couldn't be found
   * @throws ModelException
   *
   * @throws IllegalArgumentException
   *             in case if context cannot be found for the given node.
   */
  public IModelElement[] getModelElement(int startOffset, int length,
      boolean filter) throws ModelException {
    return getModelElement(new SourceRange(startOffset, length), filter);
  }

  /**
   * This method returns model elements for the expression under the given
   * offset and length. This method uses cached evaluated type from previous
   * evaluations (if exists).
   *
   * @param startOffset
   *            Starting offset of the expression.
   * @param length
   *            Length of the expression.
   * @param filter
   *            Filter the results using the 'File-Network'.
   * @param cache
   *            Model access cache if available
   * @return model element or <code>null</code> in case it couldn't be found
   * @throws ModelException
   *
   * @throws IllegalArgumentException
   *             in case if context cannot be found for the given node.
   */
  public IModelElement[] getModelElement(int startOffset, int length,
      boolean filter, IModelAccessCache cache) throws ModelException {
    return getModelElement(new SourceRange(startOffset, length), filter,
        cache);
  }

  protected IModelElement[] getModelElement(SourceRange sourceRange,
      boolean filter) throws ModelException {
    return getModelElement(sourceRange, filter, null);
  }

  protected IModelElement[] getModelElement(SourceRange sourceRange,
      boolean filter, IModelAccessCache cache) throws ModelException {
    ContextFinder contextFinder = getContext(sourceRange);
    if (!evaluatedTypesCache.containsKey(sourceRange)) {
      evaluatedTypesCache.put(
          sourceRange,
          getType(sourceRange, contextFinder.getContext(),
              contextFinder.getNode()));
    }
    IEvaluatedType evaluatedType = evaluatedTypesCache.get(sourceRange);
    return PHPTypeInferenceUtils.getModelElements(evaluatedType,
        (ISourceModuleContext) contextFinder.getContext(),
        sourceRange.getOffset(), cache);
  }

  /**
   * get the IModelElement at the given position.
   *
   * @param start
   * @param length
   * @return the IModelElement instance which represents a IField node, or
   *         null.
   * @throws Exception
   */
  public IModelElement getFieldByPosition(int start, int length)
      throws Exception {
    SourceRange sourceRange = new SourceRange(start, length);
    ContextFinder contextFinder = getContext(sourceRange);
    ASTNode node = contextFinder.getNode();

    if (node instanceof VariableReference) {
      ASTNode localScopeNode = rootNode;
      IContext context = contextFinder.getContext();
      if (context instanceof MethodContext) {
        localScopeNode = ((MethodContext) context).getMethodNode();
      }
      VariableReferenceEvaluator.LocalReferenceDeclSearcher varDecSearcher = new VariableReferenceEvaluator.LocalReferenceDeclSearcher(
          sourceModule, (VariableReference) node, localScopeNode);
      rootNode.traverse(varDecSearcher);

      Declaration[] decls = varDecSearcher.getDeclarations();
      if (decls != null && decls.length > 0) {
        return this.sourceModule.getElementAt(decls[0].getNode()
            .sourceStart());
      }
    }

    return null;
  }

  private class SourceRange {
    private final int offset;
    private final int length;

    public SourceRange(ISourceRange sourceRange) {
      offset = sourceRange.getOffset();
      length = sourceRange.getLength();
    }

    public SourceRange(int offset, int length) {
      this.length = length;
      this.offset = offset;
    }

    public SourceRange(ASTNode node) {
      this(node.sourceStart(), node.sourceEnd() - node.sourceStart());
    }

    public int getEnd() {
      return length + offset;
    }

    public int getLength() {
      return length;
    }

    public int getOffset() {
      return offset;
    }

    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + length;
      result = prime * result + offset;
      return result;
    }

    public boolean equals(Object obj) {
      if (this == obj) {
        return true;
      }
      if (obj == null) {
        return false;
      }
      if (getClass() != obj.getClass()) {
        return false;
      }
      SourceRange other = (SourceRange) obj;
      if (length != other.length) {
        return false;
      }
      if (offset != other.offset) {
        return false;
      }
      return true;
    }

    public String toString() {
      return new StringBuilder("<offset=").append(offset) //$NON-NLS-1$
          .append(", length=").append(length).append(">").toString(); //$NON-NLS-1$ //$NON-NLS-2$
    }
  }

  /**
   * Finds binding context for the given AST node for internal usages only.
   */
  private class ContextFinder extends
      org.eclipse.php.internal.core.typeinference.context.ContextFinder {

    private SourceRange sourceRange;
    private IContext context;
    private ASTNode node;

    public ContextFinder(SourceRange sourceRange) {
      super(sourceModule);
      this.sourceRange = sourceRange;
    }

    public IContext getContext() {
      if (context instanceof IModelCacheContext) {
        ((IModelCacheContext) context).setCache(modelAccessCache);
      }
      return context;
    }

    /**
     * Returns found AST node
     *
     * @return AST node
     */
    public ASTNode getNode() {
      return node;
    }

    public boolean visitGeneral(ASTNode node) throws Exception {
      if (node.sourceStart() > sourceRange.getEnd()) {
        return false;
      }
      if (node.sourceEnd() < sourceRange.offset) {
        return false;
      }
      if (node.sourceStart() <= sourceRange.offset
          && node.sourceEnd() >= sourceRange.getEnd()) {
        if (!contextStack.isEmpty()) {
          this.context = contextStack.peek();
          this.node = node;
        }
      }
      // search inside - we are looking for minimal node
      return true;
    }
  }

  public IEvaluatedType[] getFunctionReturnType(IMethod functionElement) {

    ISourceModule sourceModule = functionElement.getSourceModule();
    ModuleDeclaration sourceModuleDeclaration = SourceParserUtil
        .getModuleDeclaration(sourceModule);
    MethodDeclaration functionDeclaration = null;
    try {
      functionDeclaration = PHPModelUtils.getNodeByMethod(
          sourceModuleDeclaration, functionElement);

    } catch (ModelException e) {
      if (DLTKCore.DEBUG) {
        e.printStackTrace();
      }
    }
    // FileContext fileContext = new FileContext(sourceModule,
    // sourceModuleDeclaration);

    final List<IEvaluatedType> evaluated = new LinkedList<IEvaluatedType>();
    final List<Expression> returnExpressions = new LinkedList<Expression>();
    final List<Expression> yieldExpressions = new LinkedList<Expression>();

    if (functionDeclaration != null) {

      ASTVisitor visitor = new ASTVisitor() {
        public boolean visitGeneral(ASTNode node) throws Exception {
          if (node instanceof ReturnStatement) {
            ReturnStatement statement = (ReturnStatement) node;
            Expression expr = statement.getExpr();
            if (expr == null) {
              evaluated.add(PHPSimpleTypes.VOID);
            } else {
              returnExpressions.add(expr);
            }
          } else if (node instanceof YieldExpression) {
            YieldExpression statement = (YieldExpression) node;
            Expression expr = statement.getExpr();
            yieldExpressions.add(expr);
          }
          return super.visitGeneral(node);
        }
      };

      try {
        functionDeclaration.traverse(visitor);
      } catch (Exception e) {
        if (DLTKCore.DEBUG) {
          e.printStackTrace();
        }
      }
    }

    for (Expression expr : returnExpressions) {
      SourceRange sourceRange = new SourceRange(expr);
      try {
        ContextFinder contextFinder = getContext(sourceRange);
        IContext context = contextFinder.getContext();
        IEvaluatedType resolvedExpression = PHPTypeInferenceUtils
            .resolveExpression(cachedInferencer, sourceModule,
                sourceModuleDeclaration, context, expr);
        if (resolvedExpression != null) {
          evaluated.add(resolvedExpression);
        }
      } catch (ModelException e) {
        if (DLTKCore.DEBUG) {
          e.printStackTrace();
        }
        continue;
      }
    }
    if (yieldExpressions.size() > 0) {
      GeneratorClassType generator = new GeneratorClassType();
      for (Expression expr : yieldExpressions) {
        if (expr == null) {
          generator.getTypes().add(PHPSimpleTypes.NULL);
        } else {
          SourceRange sourceRange = new SourceRange(expr);
          try {
            ContextFinder contextFinder = getContext(sourceRange);
            IContext context = contextFinder.getContext();
            IEvaluatedType resolvedExpression = PHPTypeInferenceUtils
                .resolveExpression(cachedInferencer,
                    sourceModule, sourceModuleDeclaration,
                    context, expr);
            if (resolvedExpression != null) {
              generator.getTypes().add(resolvedExpression);
            }
          } catch (ModelException e) {
            if (DLTKCore.DEBUG) {
              e.printStackTrace();
            }
            continue;
          }
        }

        evaluated.add(generator);
      }
    }
    return (IEvaluatedType[]) evaluated
        .toArray(new IEvaluatedType[evaluated.size()]);
  }

  /**
   * Set type inferencer for this utility
   *
   * @param cachedInferencer
   */
  public void setCachedInferencer(IPHPTypeInferencer cachedInferencer) {
    this.cachedInferencer = cachedInferencer;
  }
}
TOP

Related Classes of org.eclipse.php.internal.core.typeinference.BindingUtility$SourceRange

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.