| /******************************************************************************* |
| * Copyright (c) 2010 xored software, Inc. |
| * |
| * 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: |
| * xored software, Inc. - initial API and Implementation (Alex Panchenko) |
| *******************************************************************************/ |
| package org.eclipse.dltk.internal.javascript.ti; |
| |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Stack; |
| |
| import org.eclipse.dltk.annotations.Nullable; |
| import org.eclipse.dltk.ast.ASTNode; |
| import org.eclipse.dltk.compiler.problem.IProblemIdentifier; |
| import org.eclipse.dltk.javascript.ast.ASTVisitor; |
| import org.eclipse.dltk.javascript.core.JavaScriptPlugin; |
| import org.eclipse.dltk.javascript.parser.JSProblemReporter; |
| import org.eclipse.dltk.javascript.parser.Reporter; |
| import org.eclipse.dltk.javascript.typeinference.IFunctionValueCollection; |
| import org.eclipse.dltk.javascript.typeinference.IValueCollection; |
| import org.eclipse.dltk.javascript.typeinference.IValueReference; |
| import org.eclipse.dltk.javascript.typeinfo.ITypeChecker; |
| import org.eclipse.dltk.javascript.typeinfo.ITypeInferenceHandler; |
| import org.eclipse.dltk.javascript.typeinfo.ITypeInferenceHandlerFactory; |
| import org.eclipse.dltk.javascript.typeinfo.ITypeInferenceListener; |
| import org.eclipse.dltk.javascript.typeinfo.ITypeInferencerVisitor; |
| import org.eclipse.dltk.javascript.typeinfo.TypeInfoManager; |
| |
| public abstract class TypeInferencerVisitorBase extends |
| ASTVisitor<IValueReference> implements ITypeInferencerVisitor { |
| |
| protected final ITypeInferenceContext context; |
| |
| private Stack<IValueCollection> contexts = new Stack<IValueCollection>(); |
| |
| public ITypeInferenceContext getContext() { |
| return context; |
| } |
| |
| public IValueCollection peekContext() { |
| return !contexts.isEmpty() ? contexts.peek() : null; |
| } |
| |
| public void enterContext(IValueCollection collection) { |
| contexts.push(collection); |
| } |
| |
| public IValueCollection leaveContext() { |
| return contexts.pop(); |
| } |
| |
| protected boolean inFunction() { |
| return inFunction(false); |
| } |
| |
| protected boolean inFunction(boolean ignoreBlocks) { |
| for (int i = contexts.size(); --i >= 0;) { |
| final IValueCollection collection = contexts.get(i); |
| if (collection instanceof IFunctionValueCollection) { |
| if (ignoreBlocks) { |
| if (((IFunctionValueCollection) collection).isInlineBlock()) { |
| continue; |
| } |
| } |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public TypeInferencerVisitorBase(ITypeInferenceContext context) { |
| this.context = context; |
| } |
| |
| @Nullable |
| private ITypeInferenceHandler[] handlers = null; |
| |
| @Nullable |
| ITypeInferenceListener[] listeners = null; |
| |
| public void initialize() { |
| contexts.clear(); |
| contexts.push(new TopValueCollection(context)); |
| listeners = null; |
| final List<ITypeInferenceHandler> handlers = createHandlers(); |
| if (handlers != null && !handlers.isEmpty()) { |
| this.handlers = handlers.toArray(new ITypeInferenceHandler[handlers |
| .size()]); |
| } else { |
| this.handlers = null; |
| } |
| } |
| |
| protected List<ITypeInferenceHandler> createHandlers() { |
| final List<ITypeInferenceHandler> handlers = new ArrayList<ITypeInferenceHandler>(); |
| for (ITypeInferenceHandlerFactory factory : TypeInfoManager |
| .getNodeHandlerFactories()) { |
| final ITypeInferenceHandler handler = factory.create(context, this); |
| if (handler != null) { |
| handlers.add(handler); |
| } |
| } |
| return handlers; |
| } |
| |
| public void addListener(ITypeInferenceListener listener) { |
| assert listener != null; |
| if (listeners == null) { |
| listeners = new ITypeInferenceListener[] { listener }; |
| } else { |
| final int length = this.listeners.length; |
| for (int i = 0; i < length; ++i) { |
| if (listener.equals(this.listeners[i])) { |
| return; |
| } |
| } |
| System.arraycopy(this.listeners, 0, |
| this.listeners = new ITypeInferenceListener[length + 1], 0, |
| length); |
| this.listeners[length] = listener; |
| } |
| } |
| |
| @Override |
| public IValueReference visit(ASTNode node) { |
| if (handlers != null) { |
| for (ITypeInferenceHandler handler : handlers) { |
| final IValueReference result = handler.handle(node); |
| if (result != ITypeInferenceHandler.CONTINUE) { |
| return result; |
| } |
| } |
| } |
| try { |
| return super.visit(node); |
| } catch (PositionReachedException e) { |
| throw e; |
| } catch (TIWrappedException e) { |
| throw e; |
| } catch (RuntimeException e) { |
| JavaScriptPlugin.error(buildNodeErrorMessage(node), e); |
| throw new TIWrappedException(e); |
| } catch (AssertionError e) { |
| JavaScriptPlugin.error(buildNodeErrorMessage(node), e); |
| throw new TIWrappedException(e); |
| } |
| } |
| |
| @SuppressWarnings("serial") |
| static class TIWrappedException extends RuntimeException { |
| public TIWrappedException(Throwable cause) { |
| super(cause); |
| } |
| } |
| |
| protected String buildNodeErrorMessage(ASTNode node) { |
| final StringBuilder sb = new StringBuilder(); |
| sb.append("Error processing "); |
| sb.append(node.getClass().getName()); |
| try { |
| final String message = node.toString(); |
| sb.append(" \"").append(message).append("\""); |
| } catch (Throwable t) { |
| // ignore potential errors in .toString() |
| } |
| sb.append(" in ").append(context.getSource()); |
| return sb.toString(); |
| } |
| |
| public void done() { |
| IValueCollection collection = getCollection(); |
| if (collection instanceof ValueCollection) { |
| IValue value = ((ValueCollection) collection).getValue(); |
| if (value instanceof Value) { |
| ((Value) value).resolveLazyValues(new HashSet<Value>()); |
| } |
| } |
| if (listeners != null) { |
| for (ITypeInferenceListener listener : listeners) { |
| listener.done(); |
| } |
| } |
| } |
| |
| public IValueCollection getCollection() { |
| return contexts.get(0); |
| } |
| |
| /** |
| * @param value1 |
| * @param value2 |
| * @return |
| */ |
| protected IValueReference merge(IValueReference value1, |
| IValueReference value2) { |
| final AnonymousValue reference = new AnonymousValue(); |
| reference.setValue(value1); |
| reference.addValue(value2, false); |
| return reference; |
| } |
| |
| protected JSProblemReporter reporter; |
| |
| public JSProblemReporter getProblemReporter() { |
| return reporter; |
| } |
| |
| public void suppressProblems(IProblemIdentifier... identifiers) { |
| if (reporter != null) { |
| ((Reporter) reporter).suppressProblems(identifiers); |
| } |
| } |
| |
| protected ITypeChecker typeChecker; |
| |
| /** |
| * Returns the type checker which can be used for type validation. Non-null |
| * during validation. |
| */ |
| @Nullable |
| public ITypeChecker getTypeChecker() { |
| return typeChecker; |
| } |
| |
| } |