/******************************************************************************
 * Copyright (c) 2002 - 2006 IBM Corporation.
 * 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
 *****************************************************************************/
package com.ibm.wala.cast.js.analysis.typeInference;

import com.ibm.wala.analysis.typeInference.ConeType;
import com.ibm.wala.analysis.typeInference.PointType;
import com.ibm.wala.analysis.typeInference.TypeAbstraction;
import com.ibm.wala.analysis.typeInference.TypeVariable;
import com.ibm.wala.cast.analysis.typeInference.AstTypeInference;
import com.ibm.wala.cast.js.ssa.JavaScriptCheckReference;
import com.ibm.wala.cast.js.ssa.JavaScriptInstanceOf;
import com.ibm.wala.cast.js.ssa.JavaScriptInvoke;
import com.ibm.wala.cast.js.ssa.JavaScriptPropertyRead;
import com.ibm.wala.cast.js.ssa.JavaScriptPropertyWrite;
import com.ibm.wala.cast.js.ssa.JavaScriptTypeOfInstruction;
import com.ibm.wala.cast.js.ssa.JavaScriptWithRegion;
import com.ibm.wala.cast.js.ssa.PrototypeLookup;
import com.ibm.wala.cast.js.ssa.SetPrototype;
import com.ibm.wala.cast.js.types.JavaScriptTypes;
import com.ibm.wala.fixpoint.IVariable;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.SymbolTable;
import com.ibm.wala.types.TypeReference;

public class JSTypeInference extends AstTypeInference {

  public JSTypeInference(IR ir, IClassHierarchy cha) {
    super(ir, cha, new PointType(cha.lookupClass(JavaScriptTypes.Boolean)), true);
  }

  protected void initialize() {
    class JSTypeOperatorFactory extends AstTypeOperatorFactory implements com.ibm.wala.cast.js.ssa.JSInstructionVisitor {
      public void visitJavaScriptInvoke(JavaScriptInvoke inst) {
        result = new DeclaredTypeOperator(new ConeType(cha.getRootClass()));
      }

      public void visitJavaScriptPropertyRead(JavaScriptPropertyRead inst) {
        result = new DeclaredTypeOperator(new ConeType(cha.getRootClass()));
      }

      public void visitTypeOf(JavaScriptTypeOfInstruction inst) {
        result = new DeclaredTypeOperator(new PointType(cha.lookupClass(JavaScriptTypes.String)));
      }

      public void visitJavaScriptInstanceOf(JavaScriptInstanceOf inst) {
        result = new DeclaredTypeOperator(new PointType(cha.lookupClass(JavaScriptTypes.Boolean)));
      }

      public void visitJavaScriptPropertyWrite(JavaScriptPropertyWrite inst) {
      }

      public void visitCheckRef(JavaScriptCheckReference instruction) {
      }

      public void visitWithRegion(JavaScriptWithRegion instruction) {
      }

      @Override
      public void visitSetPrototype(SetPrototype instruction) {
      }

      @Override
      public void visitPrototypeLookup(PrototypeLookup instruction) {
        result = new DeclaredTypeOperator(new ConeType(cha.getRootClass()));
      }
    }
    ;

    class JSTypeVarFactory extends TypeVarFactory {

      private TypeAbstraction make(TypeReference typeRef) {
        return new PointType(cha.lookupClass(typeRef));
      }

      public IVariable makeVariable(int vn) {
        if (ir.getSymbolTable().isStringConstant(vn)) {
          return new TypeVariable(make(JavaScriptTypes.String));
        } else if (ir.getSymbolTable().isBooleanConstant(vn)) {
          return new TypeVariable(make(JavaScriptTypes.Boolean));
        } else if (ir.getSymbolTable().isNullConstant(vn)) {
          return new TypeVariable(make(JavaScriptTypes.Null));
        } else if (ir.getSymbolTable().isNumberConstant(vn)) {
          return new TypeVariable(make(JavaScriptTypes.Number));
        } else {
          return super.makeVariable(vn);
        }
      }
    }
    ;

    init(ir, new JSTypeVarFactory(), new JSTypeOperatorFactory());
  }

  public TypeAbstraction getConstantType(int valueNumber) {
    SymbolTable st = ir.getSymbolTable();
    if (st.isStringConstant(valueNumber)) {
      return new PointType(cha.lookupClass(JavaScriptTypes.String));
    } else if (st.isBooleanConstant(valueNumber)) {
      return new PointType(cha.lookupClass(JavaScriptTypes.Boolean));
    } else if (st.isNullConstant(valueNumber)) {
      return new PointType(cha.lookupClass(JavaScriptTypes.Null));
    } else {
      return new PointType(cha.lookupClass(JavaScriptTypes.Number));
    }
  }
}
