alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Scala example source code file (TypeParser.scala)

This example Scala source code file (TypeParser.scala) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Scala tags/keywords

array, boolean, io, list, long, methodbase, msiltype, msiltype, none, symbol, symbol, todo, todo, type, type

The Scala TypeParser.scala source code

/* NSC -- new scala compiler
 * Copyright 2004-2011 LAMP/EPFL
 */


package scala.tools.nsc
package symtab
package clr

import java.io.IOException

import ch.epfl.lamp.compiler.msil.{Type => MSILType, Attribute => MSILAttribute, _}

import scala.collection.mutable.{HashMap, HashSet}
import classfile.UnPickler
import ch.epfl.lamp.compiler.msil.Type.TMVarUsage

/**
 *  @author Nikolay Mihaylov
 */
abstract class TypeParser {

  val global: Global

  import global._
  import loaders.clrTypes

  //##########################################################################

  private var clazz: Symbol = _
  private var instanceDefs: Scope = _   // was members
  private var staticModule: Symbol = _  // was staticsClass
  private var staticDefs: Scope = _     // was statics

  protected def statics: Symbol = staticModule.moduleClass

  protected var busy: Boolean = false       // lock to detect recursive reads

  private object unpickler extends UnPickler {
    val global: TypeParser.this.global.type = TypeParser.this.global
  }

  def parse(typ: MSILType, root: Symbol) {

    def handleError(e: Throwable) = {
      if (settings.debug.value) e.printStackTrace()  //debug
      throw new IOException("type '" + typ.FullName + "' is broken\n(" + e.getMessage() + ")")
    }
    assert(!busy)
    busy = true

    if (root.isModule) {
      this.clazz = root.companionClass
      this.staticModule = root
    } else {
      this.clazz = root
      this.staticModule = root.companionModule
    }
    try {
      parseClass(typ)
    } catch {
      case e: FatalError => handleError(e)
      case e: RuntimeException => handleError(e)
    }
    busy = false
  }

  class TypeParamsType(override val typeParams: List[Symbol]) extends LazyType {
    override def complete(sym: Symbol) { throw new AssertionError("cyclic type dereferencing") }
  }

  /* the names `classTParams' and `newTParams' stem from the forJVM version (ClassfileParser.sigToType())
  *  but there are differences that should be kept in mind.
  *  forMSIL, a nested class knows nothing about any type-params in the nesting class,
  *  therefore newTParams is redundant (other than for recording lexical order),
  *  it always contains the same elements as classTParams.value */
  val classTParams = scala.collection.mutable.Map[Int,Symbol]() // TODO should this be a stack? (i.e., is it possible for >1 invocation to getCLRType on the same TypeParser instance be active )
  val newTParams = new scala.collection.mutable.ListBuffer[Symbol]()
  val methodTParams = scala.collection.mutable.Map[Int,Symbol]()

  private def sig2typeBounds(tvarCILDef: GenericParamAndConstraints): Type = {
    val ts = new scala.collection.mutable.ListBuffer[Type]
    for (cnstrnt <- tvarCILDef.Constraints) {
      ts += getCLRType(cnstrnt) // TODO we're definitely not at or after erasure, no need to call objToAny, right?
    }
    TypeBounds.upper(intersectionType(ts.toList, clazz))
    // TODO variance???
  }

  private def createViewFromTo(viewSuffix : String, fromTpe : Type, toTpe : Type,
                               addToboxMethodMap : Boolean, isAddressOf : Boolean) : Symbol = {
    val flags = Flags.JAVA | Flags.STATIC | Flags.IMPLICIT; // todo: static? shouldn't be final instead?
    val viewMethodType = (msym: Symbol) => JavaMethodType(msym.newSyntheticValueParams(List(fromTpe)), toTpe)
    val vmsym = createMethod(nme.view_ + viewSuffix, flags, viewMethodType, null, true);
    // !!! this used to mutate a mutable map in definitions, but that map became
    // immutable and this kept "working" with a no-op.  So now it's commented out
    // since I retired the deprecated code which allowed for that bug.
    //
    // if (addToboxMethodMap) definitions.boxMethod(clazz) = vmsym
    
    if (isAddressOf) clrTypes.addressOfViews += vmsym
    vmsym
  }

  private def createDefaultConstructor(typ: MSILType) {
    val attrs = MethodAttributes.Public | MethodAttributes.RTSpecialName | MethodAttributes.SpecialName // TODO instance
    val declType= typ
    val method = new ConstructorInfo(declType, attrs, Array[MSILType]())
    val flags = Flags.JAVA
    val owner = clazz
    val methodSym = owner.newMethod(NoPosition, nme.CONSTRUCTOR).setFlag(flags)
    val rettype = clazz.tpe
    val mtype = methodType(Array[MSILType](), rettype);
    val mInfo = mtype(methodSym)
    methodSym.setInfo(mInfo)
    instanceDefs.enter(methodSym);
    clrTypes.constructors(methodSym) = method
  }

  private def parseClass(typ: MSILType) {

    {
      val t4c = clrTypes.types.get(clazz)
      assert(t4c == None || t4c == Some(typ))
    }
    clrTypes.types(clazz) = typ

    {
      val c4t = clrTypes.sym2type.get(typ)
      assert(c4t == None || c4t == Some(clazz))
    }
    clrTypes.sym2type(typ) = clazz

    if (typ.IsDefined(clrTypes.SCALA_SYMTAB_ATTR, false)) {
      val attrs = typ.GetCustomAttributes(clrTypes.SCALA_SYMTAB_ATTR, false);
      assert (attrs.length == 1, attrs.length);
      val a = attrs(0).asInstanceOf[MSILAttribute];
      assert (a.getConstructor() == clrTypes.SYMTAB_CONSTR);
      val symtab = a.getConstructorArguments()(0).asInstanceOf[Array[Byte]]
      unpickler.unpickle(symtab, 0, clazz, staticModule, typ.FullName);
      val mClass = clrTypes.getType(typ.FullName + "$");
      if (mClass != null) {
        clrTypes.types(statics) = mClass;
        val moduleInstance = mClass.GetField("MODULE$");
        assert (moduleInstance != null, mClass);
        clrTypes.fields(statics) = moduleInstance;
      }
      return
    }
    val flags = translateAttributes(typ)

    var clazzBoxed : Symbol = NoSymbol
    var clazzMgdPtr : Symbol = NoSymbol

    val canBeTakenAddressOf = (typ.IsValueType || typ.IsEnum) && (typ.FullName != "System.Enum")

    if(canBeTakenAddressOf) {
      clazzBoxed = clazz.owner.newClass(clazz.name.toTypeName append "Boxed")
      clazzMgdPtr = clazz.owner.newClass(clazz.name.toTypeName append "MgdPtr")
      clrTypes.mdgptrcls4clssym(clazz) =  clazzMgdPtr
      /* adding typMgdPtr to clrTypes.sym2type should happen early (before metadata for supertypes is parsed,
         before metadata for members are parsed) so that clazzMgdPtr can be found by getClRType. */
      val typMgdPtr = MSILType.mkByRef(typ)
      clrTypes.types(clazzMgdPtr) = typMgdPtr
      clrTypes.sym2type(typMgdPtr) = clazzMgdPtr
      /* clazzMgdPtr but not clazzBoxed is mapped by clrTypes.types into an msil.Type instance,
         because there's no metadata-level representation for a "boxed valuetype" */
      val instanceDefsMgdPtr = new Scope
      val classInfoMgdPtr = ClassInfoType(definitions.anyvalparam, instanceDefsMgdPtr, clazzMgdPtr)
      clazzMgdPtr.setFlag(flags)
      clazzMgdPtr.setInfo(classInfoMgdPtr)
    }

/* START CLR generics (snippet 1) */
    // first pass
    for (tvarCILDef <- typ.getSortedTVars() ) {
      val tpname = newTypeName(tvarCILDef.Name.replaceAll("!", "")) // TODO are really all type-params named in all assemblies out there? (NO)
      val tpsym = clazz.newTypeParameter(NoPosition, tpname)
      classTParams.put(tvarCILDef.Number, tpsym)
      newTParams += tpsym
      // TODO wouldn't the following also be needed later, i.e. during getCLRType
      tpsym.setInfo(definitions.AnyClass.tpe)
    }
    // second pass
    for (tvarCILDef <- typ.getSortedTVars() ) {
      val tpsym = classTParams(tvarCILDef.Number)
      tpsym.setInfo(sig2typeBounds(tvarCILDef)) // we never skip bounds unlike in forJVM
    }
/* END CLR generics (snippet 1) */
    val ownTypeParams = newTParams.toList
/* START CLR generics (snippet 2) */
    if (!ownTypeParams.isEmpty) {
      clazz.setInfo(new TypeParamsType(ownTypeParams))
      if(typ.IsValueType && !typ.IsEnum) {
        clazzBoxed.setInfo(new TypeParamsType(ownTypeParams))
      }
    }
/* END CLR generics (snippet 2) */
    instanceDefs = new Scope
    staticDefs = new Scope

    val classInfoAsInMetadata = {
        val ifaces: Array[MSILType] = typ.getInterfaces()
        val superType = if (typ.BaseType() != null) getCLRType(typ.BaseType())
                        else if (typ.IsInterface()) definitions.ObjectClass.tpe
                        else definitions.AnyClass.tpe; // this branch activates for System.Object only.
        // parents (i.e., base type and interfaces)
        val parents = new scala.collection.mutable.ListBuffer[Type]()
        parents += superType
        for (iface <- ifaces) {
          parents += getCLRType(iface)  // here the variance doesn't matter
        }
        // methods, properties, events, fields are entered in a moment
        if (canBeTakenAddressOf) {
          val instanceDefsBoxed = new Scope
          ClassInfoType(parents.toList, instanceDefsBoxed, clazzBoxed)
        } else
          ClassInfoType(parents.toList, instanceDefs, clazz)
      }

    val staticInfo = ClassInfoType(List(), staticDefs, statics)

    clazz.setFlag(flags)

    if (canBeTakenAddressOf) {
      clazzBoxed.setInfo( if (ownTypeParams.isEmpty) classInfoAsInMetadata
                          else polyType(ownTypeParams, classInfoAsInMetadata) )
      clazzBoxed.setFlag(flags)
      val rawValueInfoType = ClassInfoType(definitions.anyvalparam, instanceDefs, clazz)
      clazz.setInfo( if (ownTypeParams.isEmpty) rawValueInfoType
                     else polyType(ownTypeParams, rawValueInfoType) )
    } else {
      clazz.setInfo( if (ownTypeParams.isEmpty) classInfoAsInMetadata
                     else polyType(ownTypeParams, classInfoAsInMetadata) )
    }

    // TODO I don't remember if statics.setInfo and staticModule.setInfo should also know about type params
    statics.setFlag(Flags.JAVA)
    statics.setInfo(staticInfo)
    staticModule.setFlag(Flags.JAVA)
    staticModule.setInfo(statics.tpe)


    if (canBeTakenAddressOf) {
      //  implicit conversions are owned by staticModule.moduleClass
      createViewFromTo("2Boxed", clazz.tpe, clazzBoxed.tpe, addToboxMethodMap = true, isAddressOf = false)
      // createViewFromTo("2Object", clazz.tpe, definitions.ObjectClass.tpe, addToboxMethodMap = true, isAddressOf = false)
      createViewFromTo("2MgdPtr", clazz.tpe, clazzMgdPtr.tpe, addToboxMethodMap = false, isAddressOf = true)
      // a return can't have type managed-pointer, thus a dereference-conversion is not needed
      // similarly, a method can't declare as return type "boxed valuetype"
      if (!typ.IsEnum) {
        // a synthetic default constructor for raw-type allows `new X' syntax
        createDefaultConstructor(typ)
      }
    }

    // import nested types
    for (ntype <- typ.getNestedTypes() if !(ntype.IsNestedPrivate || ntype.IsNestedAssembly || ntype.IsNestedFamANDAssem)
				                                 || ntype.IsInterface /* TODO why shouldn't nested ifaces be type-parsed too? */ )
      {
	val loader = new loaders.MSILTypeLoader(ntype)
	val nclazz = statics.newClass(NoPosition, ntype.Name.toTypeName)
	val nmodule = statics.newModule(NoPosition, ntype.Name)
	nclazz.setInfo(loader)
	nmodule.setInfo(loader)
	staticDefs.enter(nclazz)
	staticDefs.enter(nmodule)

	assert(nclazz.companionModule == nmodule, nmodule)
	assert(nmodule.companionClass == nclazz, nclazz)
      }

    val fields = typ.getFields()
    for (field <- fields
         if !(field.IsPrivate() || field.IsAssembly() || field.IsFamilyAndAssembly)
         if (getCLRType(field.FieldType) != null)
         ) {
      assert (!field.FieldType.IsPointer && !field.FieldType.IsByRef, "CLR requirement")
      val flags = translateAttributes(field);
      val name = newTermName(field.Name);
      val fieldType =
        if (field.IsLiteral && !field.FieldType.IsEnum && isDefinedAtgetConstant(getCLRType(field.FieldType))) 
	      ConstantType(getConstant(getCLRType(field.FieldType), field.getValue))
	    else
	      getCLRType(field.FieldType)
      val owner = if (field.IsStatic()) statics else clazz;
      val sym = owner.newValue(NoPosition, name).setFlag(flags).setInfo(fieldType);
        // TODO: set private within!!! -> look at typechecker/Namers.scala
        (if (field.IsStatic()) staticDefs else instanceDefs).enter(sym);
      clrTypes.fields(sym) = field;
    }

    for (constr <- typ.getConstructors() if !constr.IsStatic() && !constr.IsPrivate() &&
         !constr.IsAssembly() && !constr.IsFamilyAndAssembly() && !constr.HasPtrParamOrRetType())
      createMethod(constr);

    // initially also contains getters and setters of properties.
    val methodsSet = new HashSet[MethodInfo]();
    methodsSet ++= typ.getMethods();

    for (prop <- typ.getProperties) {
      val propType: Type = getCLSType(prop.PropertyType);
      if (propType != null) {
	val getter: MethodInfo = prop.GetGetMethod(true);
	val setter: MethodInfo = prop.GetSetMethod(true);
	var gparamsLength: Int = -1;
	if (!(getter == null || getter.IsPrivate || getter.IsAssembly
              || getter.IsFamilyAndAssembly || getter.HasPtrParamOrRetType))
	  {
	    assert(prop.PropertyType == getter.ReturnType);
	    val gparams: Array[ParameterInfo] = getter.GetParameters();
	    gparamsLength = gparams.length;
	    val name: Name = if (gparamsLength == 0) prop.Name else nme.apply;
	    val flags = translateAttributes(getter);
	    val owner: Symbol = if (getter.IsStatic) statics else clazz;
	    val methodSym = owner.newMethod(NoPosition, name).setFlag(flags)
      val mtype: Type = if (gparamsLength == 0) NullaryMethodType(propType) // .NET properties can't be polymorphic
                        else methodType(getter, getter.ReturnType)(methodSym)
        methodSym.setInfo(mtype);
	    methodSym.setFlag(Flags.ACCESSOR);
	    (if (getter.IsStatic) staticDefs else instanceDefs).enter(methodSym)
	    clrTypes.methods(methodSym) = getter;
	    methodsSet -= getter;
	  }
	if (!(setter == null || setter.IsPrivate || setter.IsAssembly
             || setter.IsFamilyAndAssembly || setter.HasPtrParamOrRetType))
	  {
	    val sparams: Array[ParameterInfo] = setter.GetParameters()
	    if(getter != null)
	      assert(getter.IsStatic == setter.IsStatic);
	    assert(setter.ReturnType == clrTypes.VOID);
	    if(getter != null)
	      assert(sparams.length == gparamsLength + 1, "" + getter + "; " + setter);

	    val name: Name = if (gparamsLength == 0) nme.getterToSetter(prop.Name)
			     else nme.update;
	    val flags = translateAttributes(setter);
	    val mtype = methodType(setter, definitions.UnitClass.tpe);
	    val owner: Symbol = if (setter.IsStatic) statics else clazz;
	    val methodSym = owner.newMethod(NoPosition, name).setFlag(flags)
        methodSym.setInfo(mtype(methodSym))
	    methodSym.setFlag(Flags.ACCESSOR);
	    (if (setter.IsStatic) staticDefs else instanceDefs).enter(methodSym);
	    clrTypes.methods(methodSym) = setter;
	    methodsSet -= setter;
	  }
      }
    }

/*    for (event <- typ.GetEvents) {
      // adding += and -= methods to add delegates to an event.
      // raising the event ist not possible from outside the class (this is so
      // generally in .net world)
      val adder: MethodInfo = event.GetAddMethod();
      val remover: MethodInfo = event.GetRemoveMethod();
      if (!(adder == null || adder.IsPrivate || adder.IsAssembly
	    || adder.IsFamilyAndAssembly))
	{
	  assert(adder.ReturnType == clrTypes.VOID);
	  assert(adder.GetParameters().map(_.ParameterType).toList == List(event.EventHandlerType));
	  val name = encode("+=");
	  val flags = translateAttributes(adder);
	  val mtype: Type = methodType(adder, adder.ReturnType);
	  createMethod(name, flags, mtype, adder, adder.IsStatic)
	  methodsSet -= adder;
	}
      if (!(remover == null || remover.IsPrivate || remover.IsAssembly
	    || remover.IsFamilyAndAssembly))
	{
	  assert(remover.ReturnType == clrTypes.VOID);
	  assert(remover.GetParameters().map(_.ParameterType).toList == List(event.EventHandlerType));
	  val name = encode("-=");
	  val flags = translateAttributes(remover);
	  val mtype: Type = methodType(remover, remover.ReturnType);
	  createMethod(name, flags, mtype, remover, remover.IsStatic)
	  methodsSet -= remover;
	}
    } */

/* Adds view amounting to syntax sugar for a CLR implicit overload.
   The long-form syntax can also be supported if "methodsSet -= method" (last statement) is removed.

    /* remember, there's typ.getMethods and type.GetMethods  */
    for (method <- typ.getMethods)
      if(!method.HasPtrParamOrRetType &&
              method.IsPublic && method.IsStatic && method.IsSpecialName &&
              method.Name == "op_Implicit") {
        // create a view: typ => method's return type
        val viewRetType: Type = getCLRType(method.ReturnType)
        val viewParamTypes: List[Type] = method.GetParameters().map(_.ParameterType).map(getCLSType).toList;
        /* The spec says "The operator method shall be defined as a static method on either the operand or return type."
         *  We don't consider the declaring type for the purposes of definitions.functionType,
         * instead we regard op_Implicit's argument type and return type as defining the view's signature.
         */
        if (viewRetType != null && !viewParamTypes.contains(null)) {
          /* The check above applies e.g. to System.Decimal that has a conversion from UInt16, a non-CLS type, whose CLS-mapping returns null */
          val funType: Type = definitions.functionType(viewParamTypes, viewRetType);
          val flags = Flags.JAVA | Flags.STATIC | Flags.IMPLICIT; // todo: static? shouldn't be final instead?
          val viewMethodType = (msym: Symbol) => JavaMethodType(msym.newSyntheticValueParams(viewParamTypes), funType)
          val vmsym = createMethod(nme.view_, flags, viewMethodType, method, true);
          methodsSet -= method;
        }
      }
*/

    for (method <- methodsSet.iterator)
      if (!method.IsPrivate() && !method.IsAssembly() && !method.IsFamilyAndAssembly()
           && !method.HasPtrParamOrRetType)
        createMethod(method);

    // Create methods and views for delegate support
    if (clrTypes.isDelegateType(typ)) {
      createDelegateView(typ)
      createDelegateChainers(typ)
    }

    // for enumerations introduce comparison and bitwise logical operations;
    // the backend will recognize them and replace them with comparison or
    // bitwise logical operations on the primitive underlying type

    if (typ.IsEnum) {
      val ENUM_CMP_NAMES = List(nme.EQ, nme.NE, nme.LT, nme.LE, nme.GT, nme.GE);
      val ENUM_BIT_LOG_NAMES = List(nme.OR, nme.AND, nme.XOR);

      val flags = Flags.JAVA | Flags.FINAL
      for (cmpName <- ENUM_CMP_NAMES) {
        val enumCmp = clazz.newMethod(NoPosition, cmpName)
        val enumCmpType = JavaMethodType(enumCmp.newSyntheticValueParams(List(clazz.tpe)), definitions.BooleanClass.tpe)
        enumCmp.setFlag(flags).setInfo(enumCmpType)
        instanceDefs.enter(enumCmp)
      }

      for (bitLogName <- ENUM_BIT_LOG_NAMES) {
        val enumBitLog = clazz.newMethod(NoPosition, bitLogName)
        val enumBitLogType = JavaMethodType(enumBitLog.newSyntheticValueParams(List(clazz.tpe)), clazz.tpe /* was classInfo, infinite typer */)
        enumBitLog.setFlag(flags).setInfo(enumBitLogType)
        instanceDefs.enter(enumBitLog)
      }
    }

  } // parseClass

  private def populateMethodTParams(method: MethodBase, methodSym: MethodSymbol) : List[Symbol] = {
    if(!method.IsGeneric) Nil
    else {
      methodTParams.clear
      val newMethodTParams = new scala.collection.mutable.ListBuffer[Symbol]()

      // first pass
      for (mvarCILDef <- method.getSortedMVars() ) {
        val mtpname = newTypeName(mvarCILDef.Name.replaceAll("!", "")) // TODO are really all method-level-type-params named in all assemblies out there? (NO)
        val mtpsym = methodSym.newTypeParameter(NoPosition, mtpname)
        methodTParams.put(mvarCILDef.Number, mtpsym)
        newMethodTParams += mtpsym
        // TODO wouldn't the following also be needed later, i.e. during getCLRType
        mtpsym.setInfo(definitions.AnyClass.tpe)
      }
      // second pass
      for (mvarCILDef <- method.getSortedMVars() ) {
        val mtpsym = methodTParams(mvarCILDef.Number)
        mtpsym.setInfo(sig2typeBounds(mvarCILDef)) // we never skip bounds unlike in forJVM
      }

      newMethodTParams.toList
    }
  }

  private def createMethod(method: MethodBase) {

    val flags = translateAttributes(method);
    val owner = if (method.IsStatic()) statics else clazz;
    val methodSym = owner.newMethod(NoPosition, getName(method)).setFlag(flags)
    /* START CLR generics (snippet 3) */
    val newMethodTParams = populateMethodTParams(method, methodSym)
    /* END CLR generics (snippet 3) */

    val rettype = if (method.IsConstructor()) clazz.tpe
                  else getCLSType(method.asInstanceOf[MethodInfo].ReturnType);
    if (rettype == null) return;
    val mtype = methodType(method, rettype);
    if (mtype == null) return;
/* START CLR generics (snippet 4) */
    val mInfo = if (method.IsGeneric) polyType(newMethodTParams, mtype(methodSym))
                else mtype(methodSym)
/* END CLR generics (snippet 4) */
/* START CLR non-generics (snippet 4)
    val mInfo = mtype(methodSym) 
   END CLR non-generics (snippet 4) */
    methodSym.setInfo(mInfo)
    (if (method.IsStatic()) staticDefs else instanceDefs).enter(methodSym);
    if (method.IsConstructor())
      clrTypes.constructors(methodSym) = method.asInstanceOf[ConstructorInfo]
    else clrTypes.methods(methodSym) = method.asInstanceOf[MethodInfo];
  }

  private def createMethod(name: Name, flags: Long, args: Array[MSILType], retType: MSILType, method: MethodInfo, statik: Boolean): Symbol = {
    val mtype = methodType(args, getCLSType(retType))
    assert(mtype != null)
    createMethod(name, flags, mtype, method, statik)
  }

  private def createMethod(name: Name, flags: Long, mtype: Symbol => Type, method: MethodInfo, statik: Boolean): Symbol = {
    val methodSym: Symbol = (if (statik)  statics else clazz).newMethod(NoPosition, name)
    methodSym.setFlag(flags).setInfo(mtype(methodSym))
    (if (statik) staticDefs else instanceDefs).enter(methodSym)
    if (method != null)
      clrTypes.methods(methodSym)  = method
    methodSym
  }

  private def createDelegateView(typ: MSILType) = {
    val invoke: MethodInfo = typ.GetMember("Invoke")(0).asInstanceOf[MethodInfo];
    val invokeRetType: Type = getCLRType(invoke.ReturnType);
    val invokeParamTypes: List[Type] =invoke.GetParameters().map(_.ParameterType).map(getCLSType).toList;
    val funType: Type = definitions.functionType(invokeParamTypes, invokeRetType);

    val typClrType: Type = getCLRType(typ);
    val flags = Flags.JAVA | Flags.STATIC | Flags.IMPLICIT; // todo: static? think not needed

    // create the forward view: delegate => function
    val delegateParamTypes: List[Type] = List(typClrType);
    // not ImplicitMethodType, this is for methods with implicit parameters (not implicit methods)
    val forwardViewMethodType = (msym: Symbol) => JavaMethodType(msym.newSyntheticValueParams(delegateParamTypes), funType)
    val fmsym = createMethod(nme.view_, flags, forwardViewMethodType, null, true);

    // create the backward view: function => delegate
    val functionParamTypes: List[Type] = List(funType);
    val backwardViewMethodType = (msym: Symbol) => JavaMethodType(msym.newSyntheticValueParams(functionParamTypes), typClrType)
    val bmsym = createMethod(nme.view_, flags, backwardViewMethodType, null, true);
  }

  private def createDelegateChainers(typ: MSILType) = {
    val flags: Long = Flags.JAVA | Flags.FINAL
    val args: Array[MSILType] = Array(typ)

    var s = createMethod(encode("+="), flags, args, clrTypes.VOID, clrTypes.DELEGATE_COMBINE, false);
    s = createMethod(encode("-="), flags, args, clrTypes.VOID, clrTypes.DELEGATE_REMOVE, false);

    s = createMethod(nme.PLUS, flags, args, typ, clrTypes.DELEGATE_COMBINE, false);
    s = createMethod(nme.MINUS, flags, args, typ, clrTypes.DELEGATE_REMOVE, false);
  }

  private def getName(method: MethodBase): Name = {

    def operatorOverload(name : String, paramsArity : Int) : Option[Name] = paramsArity match {
      case 1 => name match {
        // PartitionI.10.3.1
        case "op_Decrement" => Some(encode("--"))
        case "op_Increment" => Some(encode("++"))
        case "op_UnaryNegation" => Some(nme.UNARY_-)
        case "op_UnaryPlus" => Some(nme.UNARY_+)
        case "op_LogicalNot" => Some(nme.UNARY_!)
        case "op_OnesComplement" => Some(nme.UNARY_~)
        /* op_True and op_False have no operator symbol assigned,
           Other methods that will have to be written in full are:
           op_AddressOf & (unary)
           op_PointerDereference * (unary) */
        case _ => None
      }
      case 2 => name match {
        // PartitionI.10.3.2
        case "op_Addition" => Some(nme.ADD)
        case "op_Subtraction" => Some(nme.SUB)
        case "op_Multiply" => Some(nme.MUL)
        case "op_Division" => Some(nme.DIV)
        case "op_Modulus" => Some(nme.MOD)
        case "op_ExclusiveOr" => Some(nme.XOR)
        case "op_BitwiseAnd" => Some(nme.AND)
        case "op_BitwiseOr" => Some(nme.OR)
        case "op_LogicalAnd" => Some(nme.ZAND)
        case "op_LogicalOr" => Some(nme.ZOR)
        case "op_LeftShift" => Some(nme.LSL)
        case "op_RightShift" => Some(nme.ASR)
        case "op_Equality" => Some(nme.EQ)
        case "op_GreaterThan" => Some(nme.GT)
        case "op_LessThan" => Some(nme.LT)
        case "op_Inequality" => Some(nme.NE)
        case "op_GreaterThanOrEqual" => Some(nme.GE)
        case "op_LessThanOrEqual" => Some(nme.LE)

        /* op_MemberSelection is reserved in Scala  */

        /* The standard does not assign operator symbols to op_Assign , op_SignedRightShift , op_UnsignedRightShift ,
         *   and op_UnsignedRightShiftAssignment so those names will be used instead to invoke those methods. */

        /*
          The remaining binary operators are not overloaded in C# and are therefore not in widespread use. They have to be written in full.

          op_RightShiftAssignment      >>=
          op_MultiplicationAssignment  *=
          op_PointerToMemberSelection  ->*
          op_SubtractionAssignment     -=
          op_ExclusiveOrAssignment     ^=
          op_LeftShiftAssignment       <<=
          op_ModulusAssignment         %=
          op_AdditionAssignment        +=
          op_BitwiseAndAssignment      &=
          op_BitwiseOrAssignment       |=
          op_Comma                     ,
          op_DivisionAssignment        /=
        */
        case _ => None
      }
      case _ => None
    }

    if (method.IsConstructor()) return nme.CONSTRUCTOR;
    val name = method.Name;
    if (method.IsStatic()) {
      if(method.IsSpecialName) {
        val paramsArity = method.GetParameters().size
        // handle operator overload, otherwise handle as any static method
        val operName = operatorOverload(name, paramsArity)
        if (operName.isDefined) { return operName.get; }
      }
      return newTermName(name);
    }
    val params = method.GetParameters();
    name match {
      case "GetHashCode" if (params.length == 0) => nme.hashCode_;
      case "ToString" if (params.length == 0) => nme.toString_;
      case "Finalize" if (params.length == 0) => nme.finalize_;
      case "Equals" if (params.length == 1 && params(0).ParameterType == clrTypes.OBJECT) =>
        nme.equals_;
      case "Invoke" if (clrTypes.isDelegateType(method.DeclaringType)) => nme.apply;
      case _ => newTermName(name);
    }
  }

  //##########################################################################

  private def methodType(method: MethodBase, rettype: MSILType): Symbol => Type = {
    val rtype = getCLSType(rettype);
    if (rtype == null) null else methodType(method, rtype);
  }

  /** Return a method type for the given method. */
  private def methodType(method: MethodBase, rettype: Type): Symbol => Type =
    methodType(method.GetParameters().map(_.ParameterType), rettype);

  /** Return a method type for the provided argument types and return type. */
  private def methodType(argtypes: Array[MSILType], rettype: Type): Symbol => Type = {
    def paramType(typ: MSILType): Type =
      if (typ eq clrTypes.OBJECT) definitions.AnyClass.tpe // TODO a hack to compile scalalib, should be definitions.AnyRefClass.tpe  
      else getCLSType(typ);
    val ptypes = argtypes.map(paramType).toList;
    if (ptypes.contains(null)) null
    else method => JavaMethodType(method.newSyntheticValueParams(ptypes), rettype);
  }

    //##########################################################################

  private def getClassType(typ: MSILType): Type = {
    assert(typ != null);
    val res = definitions.getClass(typ.FullName.replace('+', '.')).tpe;
    //if (res.isError())
    //  global.reporter.error("unknown class reference " + type.FullName);
    res
  }

  private def getCLSType(typ: MSILType): Type = { // getCLS returns non-null for types GenMSIL can handle, be they CLS-compliant or not
    if (typ.IsTMVarUsage())
    /* START CLR generics (snippet 5) */
      getCLRType(typ)
    /* END CLR generics (snippet 5) */
    /* START CLR non-generics (snippet 5)
      null
       END CLR non-generics (snippet 5) */
    else if ( /* TODO hack if UBYE, uncommented, "ambiguous reference to overloaded definition" ensues, for example for System.Math.Max(x, y) */
              typ == clrTypes.USHORT || typ == clrTypes.UINT || typ == clrTypes.ULONG
      /*  || typ == clrTypes.UBYTE    */
          ||  typ.IsNotPublic()      || typ.IsNestedPrivate()
          ||  typ.IsNestedAssembly() || typ.IsNestedFamANDAssem()
          ||  typ.IsPointer()
          || (typ.IsArray() && getCLRType(typ.GetElementType()) == null)  /* TODO hack: getCLR instead of getCLS */
          || (typ.IsByRef() && !typ.GetElementType().CanBeTakenAddressOf()))
      null 
    else
      getCLRType(typ)
  }

  private def getCLRTypeIfPrimitiveNullOtherwise(typ: MSILType): Type =
    if (typ == clrTypes.OBJECT)
      definitions.ObjectClass.tpe;
    else if (typ == clrTypes.VALUE_TYPE)
      definitions.AnyValClass.tpe
    else if (typ == clrTypes.STRING)
      definitions.StringClass.tpe;
    else if (typ == clrTypes.VOID)
      definitions.UnitClass.tpe
    else if (typ == clrTypes.BOOLEAN)
      definitions.BooleanClass.tpe
    else if (typ == clrTypes.CHAR)
      definitions.CharClass.tpe
    else if ((typ == clrTypes.BYTE)  || (typ == clrTypes.UBYTE)) // TODO U... is a hack to compile scalalib
      definitions.ByteClass.tpe
    else if ((typ == clrTypes.SHORT) || (typ == clrTypes.SHORT)) // TODO U... is a hack to compile scalalib
      definitions.ShortClass.tpe
    else if ((typ == clrTypes.INT)   || (typ == clrTypes.UINT))  // TODO U... is a hack to compile scalalib
      definitions.IntClass.tpe
    else if ((typ == clrTypes.LONG)  || (typ == clrTypes.LONG))  // TODO U... is a hack to compile scalalib
      definitions.LongClass.tpe
    else if (typ == clrTypes.FLOAT)
      definitions.FloatClass.tpe
    else if (typ == clrTypes.DOUBLE)
      definitions.DoubleClass.tpe
    else null


  private def getCLRType(tMSIL: MSILType): Type = {
     var res = getCLRTypeIfPrimitiveNullOtherwise(tMSIL)
     if (res != null) res
     else if (tMSIL.isInstanceOf[ConstructedType]) {
       val ct = tMSIL.asInstanceOf[ConstructedType]
       /* START CLR generics (snippet 6) */
             val cttpArgs = ct.typeArgs.map(tmsil => getCLRType(tmsil)).toList
             appliedType(getCLRType(ct.instantiatedType), cttpArgs)
       /* END CLR generics (snippet 6) */
       /* START CLR non-generics (snippet 6)
       getCLRType(ct.instantiatedType)
          END CLR non-generics (snippet 6) */
     } else if (tMSIL.isInstanceOf[TMVarUsage]) {
        /* START CLR generics (snippet 7) */
             val tVarUsage = tMSIL.asInstanceOf[TMVarUsage]
             val tVarNumber = tVarUsage.Number
             if (tVarUsage.isTVar) classTParams(tVarNumber).typeConstructor // shouldn't fail, just return definitions.AnyClass.tpe at worst
             else methodTParams(tVarNumber).typeConstructor // shouldn't fail, just return definitions.AnyClass.tpe at worst
        /* END CLR generics (snippet 7) */
       /* START CLR non-generics (snippet 7)
        null // definitions.ObjectClass.tpe 
          END CLR non-generics (snippet 7) */
     } else if (tMSIL.IsArray()) {
        var elemtp = getCLRType(tMSIL.GetElementType())
        // cut&pasted from ClassfileParser
        // make unbounded Array[T] where T is a type variable into Array[T with Object]
        // (this is necessary because such arrays have a representation which is incompatible
        // with arrays of primitive types).
        // TODO does that incompatibility also apply to .NET?   
        if (elemtp.typeSymbol.isAbstractType && !(elemtp <:< definitions.ObjectClass.tpe))
          elemtp = intersectionType(List(elemtp, definitions.ObjectClass.tpe))
        appliedType(definitions.ArrayClass.tpe, List(elemtp))
     } else {
       res = clrTypes.sym2type.get(tMSIL) match {
         case Some(sym) => sym.tpe
         case None => if (tMSIL.IsByRef && tMSIL.GetElementType.IsValueType) {
                        val addressed = getCLRType(tMSIL.GetElementType)
                        val clasym = addressed.typeSymbolDirect // TODO should be .typeSymbol?
                        clasym.info.load(clasym)
                        val secondAttempt = clrTypes.sym2type.get(tMSIL)
                        secondAttempt match { case Some(sym) => sym.tpe
                                              case None => null
                                            }
                      } else getClassType(tMSIL)
       }
       if (res == null)
         null // TODO new RuntimeException()
       else res
     }
  }

  // the values are Java-Box-Classes (e.g. Integer, Boolean, Character)
  // java.lang.Number to get the value (if a number, not for boolean, character)
  // see ch.epfl.lamp.compiler.msil.util.PEStream.java
  def getConstant(constType: Type, value: Object): Constant = {
    val typeClass = constType.typeSymbol
    if (typeClass == definitions.BooleanClass)
      Constant(value.asInstanceOf[java.lang.Boolean].booleanValue)
    else if (typeClass == definitions.ByteClass)
      Constant(value.asInstanceOf[java.lang.Number].byteValue)
    else if (typeClass == definitions.ShortClass)
      Constant(value.asInstanceOf[java.lang.Number].shortValue)
    else if (typeClass == definitions.CharClass)
      Constant(value.asInstanceOf[java.lang.Character].charValue)
    else if (typeClass == definitions.IntClass)
      Constant(value.asInstanceOf[java.lang.Number].intValue)
    else if (typeClass == definitions.LongClass)
      Constant(value.asInstanceOf[java.lang.Number].longValue)
    else if (typeClass == definitions.FloatClass)
      Constant(value.asInstanceOf[java.lang.Number].floatValue)
    else if (typeClass == definitions.DoubleClass)
      Constant(value.asInstanceOf[java.lang.Number].doubleValue)
    else if (typeClass == definitions.StringClass)
      Constant(value.asInstanceOf[java.lang.String])
    else
      abort("illegal value: " + value + ", class-symbol: " + typeClass)
  }

  def isDefinedAtgetConstant(constType: Type): Boolean = {
    val typeClass = constType.typeSymbol
    if (    (typeClass == definitions.BooleanClass)
         || (typeClass == definitions.ByteClass)
         || (typeClass == definitions.ShortClass)
         || (typeClass == definitions.CharClass)
         || (typeClass == definitions.IntClass)
         || (typeClass == definitions.LongClass)
         || (typeClass == definitions.FloatClass)
         || (typeClass == definitions.DoubleClass)
         || (typeClass == definitions.StringClass)
       )
      true
    else
      false
  }

  private def translateAttributes(typ: MSILType): Long = {
    var flags: Long = Flags.JAVA;
    if (typ.IsNotPublic() || typ.IsNestedPrivate()
	|| typ.IsNestedAssembly() || typ.IsNestedFamANDAssem())
      flags = flags | Flags.PRIVATE;
    else if (typ.IsNestedFamily() || typ.IsNestedFamORAssem())
      flags = flags | Flags.PROTECTED;
    if (typ.IsAbstract())
      flags = flags | Flags.ABSTRACT;
    if (typ.IsSealed())
      flags = flags | Flags.FINAL;
    if (typ.IsInterface())
      flags = flags | Flags.INTERFACE | Flags.TRAIT | Flags.ABSTRACT;

    flags
  }

  private def translateAttributes(field: FieldInfo): Long = {
    var flags: Long = Flags.JAVA;
    if (field.IsPrivate() || field.IsAssembly() || field.IsFamilyAndAssembly())
      flags = flags | Flags.PRIVATE;
    else if (field.IsFamily() || field.IsFamilyOrAssembly())
      flags = flags | Flags.PROTECTED;
    if (field.IsInitOnly() || field.IsLiteral())
      flags = flags | Flags.FINAL;
    else
      flags = flags | Flags.MUTABLE;
    if (field.IsStatic)
      flags = flags | Flags.STATIC

    flags
  }

  private def translateAttributes(method: MethodBase): Long = {
    var flags: Long = Flags.JAVA;
    if (method.IsPrivate() || method.IsAssembly() || method.IsFamilyAndAssembly())
      flags = flags | Flags.PRIVATE;
    else if (method.IsFamily() || method.IsFamilyOrAssembly())
      flags = flags | Flags.PROTECTED;
    if (method.IsAbstract())
      flags = flags | Flags.DEFERRED;
    if (method.IsStatic)
      flags = flags | Flags.STATIC

    flags
  }
}

Other Scala examples (source code examples)

Here is a short list of links related to this Scala TypeParser.scala source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.