FunctionSymbol.java :  » UnTagged » simplecompiler2 » com » google » devtools » simple » compiler » symbols » Android Open Source

Android Open Source » UnTagged » simplecompiler2 
simplecompiler2 » com » google » devtools » simple » compiler » symbols » FunctionSymbol.java
/*
 * Copyright 2009 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.devtools.simple.compiler.symbols;

import com.google.devtools.simple.classfiles.ClassFile;
import com.google.devtools.simple.classfiles.Method;
import com.google.devtools.simple.classfiles.MethodTooBigException;
import com.google.devtools.simple.classfiles.Method.Label;
import com.google.devtools.simple.compiler.Compiler;
import com.google.devtools.simple.compiler.Error;
import com.google.devtools.simple.compiler.scanner.Scanner;
import com.google.devtools.simple.compiler.scanner.TokenKind;
import com.google.devtools.simple.compiler.scopes.LocalScope;
import com.google.devtools.simple.compiler.RuntimeLoader;
import com.google.devtools.simple.compiler.scopes.Scope;
import com.google.devtools.simple.compiler.statements.OnErrorStatement;
import com.google.devtools.simple.compiler.statements.StatementBlock;
import com.google.devtools.simple.compiler.statements.synthetic.LocalVariableDefinitionStatement;
import com.google.devtools.simple.compiler.statements.synthetic.MarkerStatement;
import com.google.devtools.simple.compiler.types.FunctionType;
import com.google.devtools.simple.compiler.types.Type;
import com.google.devtools.simple.util.Preconditions;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;

/**
 * Superclass for all function symbols.
 *
 * @author Herbert Czymontek
 */
public abstract class FunctionSymbol extends Symbol implements SymbolWithType {

  /*
   * This is a local helper class used to store information that available for compiled functions
   * only. Separating this information results in smaller function symbols for functions defined
   * from loaded class files.
   */
  private class CompilationInformation {
    // Number of variables used by function
    private short varCount;

    // List of formal parameters
    private final List<LocalVariableSymbol> params;

    // List of local variables
    private final List<LocalVariableSymbol> locals;

    // Function scope
    private final LocalScope scope;

    // 'Me' variable - can be null for static functions
    private LocalVariableSymbol me;

    // Local variable for function results (will be null for procedures)
    private LocalVariableSymbol returnVariable;

    // Statements in function body (can be null for interface function definitions)
    private StatementBlock statements;

    // Optional On Error statement (one per function)
    private OnErrorStatement onErrorStatement;

    // Begin of function scope marker statement
    private final MarkerStatement beginFunctionMarker;

    // Label to branch to for function Exit-statement
    private Method.Label exitLabel;

    // Indicates whether the function is a property getter or setter (properties need different
    // attributes generated)
    private boolean isProperty;

    // Indicates whether the function is compiler generated
    private boolean isGenerated;

    /*
     * Creates a new compilation information record (as the name suggests, for compiled functions
     * only).
     */
    private CompilationInformation() {
      params = new ArrayList<LocalVariableSymbol>();
      locals = new ArrayList<LocalVariableSymbol>();
      statements = new StatementBlock(definingObject.getScope());
      exitLabel = Method.newLabel();
      scope = (LocalScope) statements.getScope();

      // Will mark the start-of-scope for all parameters
      beginFunctionMarker = new MarkerStatement();
      statements.add(beginFunctionMarker);

      // Add a 'Me' variable for instance functions
      if (hasMeArgument()) {
        // Even though we call this variable 'Me', the Java debugger gets confused if it is not
        // called this. Specifically, JDI will report that there is a "this" and also a variable
        // named "Me.
        me = addVariable(getPosition(), "this", definingObject.getType(), false);
        me.setBeginScope(beginFunctionMarker);

        // Even though it will never be looked up via its scope, 'Me' needs to be entered because
        // the scope is responsible for setting variable scope information for debug information
        // generation.
        scope.enterSymbol(me);
      }
    }

    /*
     * Adds a function parameter.
     */
    private LocalVariableSymbol addParameter(long position, String parName, Type parType,
        boolean isRef) {
      LocalVariableSymbol param = addVariable(position, parName, parType, isRef);
      param.setBeginScope(beginFunctionMarker);
      referenceParameters.set(params.size(), isRef);
      params.add(param);

      if (scope.lookupShallow(parName) != null) {
        return null;
      }

      scope.enterSymbol(param);
      return param;
    }

    /*
     * Adds a new local variable.
     */
    private LocalVariableSymbol addLocalVariable(long position, String varName, Type varType,
        Scope varScope) {
      LocalVariableSymbol local = addVariable(position, varName, varType, false);
      locals.add(local);

      if (varScope.lookupShallow(varName) != null) {
        return null;
      }

      varScope.enterSymbol(local);
      return local;
    }

    /*
     * Adds a new variable (either parameter or local). This method should only be called from
     * within CompilationInformation.
     */
    private LocalVariableSymbol addVariable(long position, String varName, Type varType,
        boolean isRef) {
      short varIndex = varCount;
      varCount += (varType.isWideType() && !isRef) ? 2 : 1;
      return new LocalVariableSymbol(position, varName, varType, varIndex, isRef);
    }

    /*
     * Resolves the formal parameter list.
     */
    private void resolveParameters(Compiler compiler) {
      for (LocalVariableSymbol param : params) {
        param.resolve(compiler, FunctionSymbol.this);
        formalArgumentTypes.add(param.getType());
      }
    }

    /*
     * Resolves the function body (including local variables).
     */
    private void resolveBody(Compiler compiler) {
      if (statements != null) {
        // First resolve local variables
        for (LocalVariableSymbol local: locals) {
          local.resolve(compiler, FunctionSymbol.this);
        }

        // The resolve the actual function body
        statements.resolve(compiler, FunctionSymbol.this, null);

        if (onErrorStatement != null) {
          onErrorStatement.resolve(compiler, FunctionSymbol.this);
        }
      }
    }
  }

  // Object defining the function
  private ObjectSymbol definingObject;

  // List of formal argument types
  private final List<Type> formalArgumentTypes;

  // Indicates reference parameters.
  private final BitSet referenceParameters;

  // Result type of function (null for procedures)
  private Type resultType;

  // Additional information about the function body for compiled functions
  private CompilationInformation compilationInformation;

  // Function type
  private Type type;

  /**
   * Creates a new function symbol.
   *
   * @param position  source code start position of symbol
   * @param definingObject  defining object
   * @param name  function name
   */
  public FunctionSymbol(long position, ObjectSymbol definingObject, String name) {
    super(position, name);

    this.definingObject = definingObject;

    formalArgumentTypes = new ArrayList<Type>();
    referenceParameters = new BitSet();
  }

  /**
   * Marks the function as a compiled function.
   */
  public void setIsCompiled() {
    compilationInformation = new CompilationInformation();
  }

  /**
   * Marks the function as being abstract (interface function).
   */
  public void setIsAbstract() {
    compilationInformation.statements = null;
  }

  /**
   * Marks the function as a property getter or setter.
   */
  public void setIsProperty() {
    // This will cause generation of the @SimpleProperty annotation rather than @SimpleFunction.
    compilationInformation.isProperty = true;
  }

  /**
   * Marks the function as being compiler generated.
   */
  public void setIsGenerated() {
    Preconditions.checkNotNull(compilationInformation);
    compilationInformation.isGenerated = true;
  }

  /**
   * Sets the On Error statement for a compiled function.
   *
   * @param onErrorStatement  On-Error statement
   * @return  {@code false} if there is another On-Error statement in this
   *          function already, {@code true} otherwise
   */
  public boolean setOnErrorStatement(OnErrorStatement onErrorStatement) {
    Preconditions.checkNotNull(compilationInformation);

    if (compilationInformation.onErrorStatement != null) {
      return false;
    }

    compilationInformation.onErrorStatement = onErrorStatement;
    return true;
  }

  /**
   * Sets the function result type.
   *
   * @param type  function result type
   */
  public void setResultType(Type type) {
    resultType = type;
    if (compilationInformation != null) {
      // If this is a compiled function we also need to allocate a local variable of the same
      // name as the function to hold the function result
      LocalVariableSymbol returnVariable = addLocalVariable(getPosition(), getName(), type,
          getScope());
      MarkerStatement marker = new LocalVariableDefinitionStatement(returnVariable);
      compilationInformation.statements.add(marker);
      returnVariable.setBeginScope(marker);
      compilationInformation.returnVariable = returnVariable;
    }
  }

  /**
   * Change the defining object for this function. This is needed when moving an
   * event handler into a synthetic inner class.
   *
   * @param object  new defining object
   */
  public void changeDefiningObject(ObjectSymbol object) {
    definingObject = object;
  }

  /**
   * Indicates whether this function is a compiled function (as opposed to
   * loaded from a library).
   *
   * @return  {@code true} for compiled functions, {@code false} otherwise
   */
  public boolean isCompiled() {
    return compilationInformation != null;
  }

  /**
   * Indicates whether the functions is an instance or object function.
   *
   * @return  {@code true} for instance functions, {@code false} otherwise
   */
  public boolean hasMeArgument() {
    return false;
  }

  /**
   * Returns the function result type.
   *
   * @return  function result type or {@code null} for procedures
   */
  public Type getResultType() {
    return resultType;
  }

  /**
   * Adds a formal parameter.
   *
   * @param position  source code start position of symbol
   * @param parName  parameter name (can be {@code null} for loaded functions)
   * @param parType  parameter type
   * @param isRef  indicates reference parameter
   * @return  parameter symbol or {@code null} for redefiniton errors
   *          (note: will always be {code null} for loaded functions which does
   *          not indicate an error)
   */
  public LocalVariableSymbol addParameter(long position, String parName, Type parType,
      boolean isRef) {
    if (compilationInformation != null) {
      return compilationInformation.addParameter(position, parName, parType, isRef);
    } else {
      referenceParameters.set(formalArgumentTypes.size(), isRef);
      formalArgumentTypes.add(parType);
      return null;
    }
  }

  /**
   * Adds a local variable. May not be called for loaded functions!
   *
   * @param position  source code start position of symbol
   * @param varName  local variable name
   * @param varType  local variable type
   * @param scope  scope to enter local variable into
   * @return  local variable symbol
   */
  public LocalVariableSymbol addLocalVariable(long position, String varName, Type varType,
      Scope scope) {
    Preconditions.checkNotNull(compilationInformation);

    return compilationInformation.addLocalVariable(position, varName, varType, scope);
  }

  /**
   * Adds a temporary local variable. Should be used to allocated additional
   * variables needed for code generation. Must be called during the resolution
   * phase.
   *
   * @param varType  type of temporary
   * @return  temporary variable symbol
   */
  public LocalVariableSymbol addTempVariable(Type varType) {
    return compilationInformation.addVariable(Scanner.NO_POSITION, "<temp>", varType, false);
  }

  /**
   * Returns the object defining the data member.
   *
   * @return  defining object
   */
  public ObjectSymbol getDefiningObject() {
    return definingObject;
  }

  /**
   * Returns the function scope.
   *
   * @return  function scope
   */
  public Scope getScope() {
    return compilationInformation.scope;
  }

  /**
   * Returns the statements in the function body.
   *
   * @return  function body statement block (can be {@code null} for abstract
   *          functions)
   */
  public StatementBlock getFunctionStatements() {
    return compilationInformation.statements;
  }

  @Override
  public void resolve(Compiler compiler, FunctionSymbol currentFunction) {
    if (type == null) {
      // For compiled functions resolve formal parameters (will resolve types needed to resolve
      // function type)
      if (compilationInformation != null) {
        compilationInformation.resolveParameters(compiler);
      }

      // Create and resolve function type
      type = new FunctionType(hasMeArgument(), formalArgumentTypes, referenceParameters,
          resultType);
      type.resolve(compiler);
    }
  }

  /**
   * Resolves the body of compiled functions.
   *
   * @param compiler  current compiler instance
   */
  public void resolveBody(Compiler compiler) {
    if (compilationInformation != null) {
      compilationInformation.resolveBody(compiler);
    }
  }

  /**
   * Returns the exit label for a function Exit-statement.
   *
   * @return  exit label
   */
  public Method.Label getExitLabel() {
    return compilationInformation.exitLabel;
  }

  /**
   * Returns the token to be expected after an Exit statement for the function.
   *
   * @return  exit token
   */
  public TokenKind getExitToken() {
    if (compilationInformation.isProperty) {
      return TokenKind.TOK_PROPERTY;
    } else if (resultType != null) {
      return TokenKind.TOK_FUNCTION;
    } else {
      return TokenKind.TOK_SUB;
    }
  }

  @Override
  public void generateRead(Method m) {
    Compiler.internalError();  // COV_NF_LINE
  }

  @Override
  public void generateWrite(Method m) {
    Compiler.internalError();  // COV_NF_LINE
  }

  /**
   * Generates code for the function.
   *
   * @param compiler  current compiler instance
   * @param cf  classfile to generate the code into
   */
  public void generate(Compiler compiler, ClassFile cf) {
    // By default all Simple functions are public
    short access = Method.ACC_PUBLIC;

    // Object functions are static
    if (!hasMeArgument()) {
      access |= Method.ACC_STATIC;
    }

    // Interface functions are abstract
    if (compilationInformation.statements == null) {
      access |= Method.ACC_ABSTRACT;
    }

    Method m = cf.newMethod(access, getName(), type.signature());
    // Mark function/property as a Simple function/property
    if (!compilationInformation.isGenerated) {
      m.getRuntimeVisibleAnnotationsAttribute().newAnnotation(
          'L' + RuntimeLoader.ANNOTATION_INTERNAL +
          (compilationInformation.isProperty ? "/SimpleProperty;" : "/SimpleFunction;"));
    }

    // If there is a function body then generate bytecode
    if (compilationInformation.statements != null) {
      try {
        m.startCodeGeneration();

        // If there is an On Error statement then we must explicitly initialize all local
        // variables to appease the verifier (even if they later re-initialized).
        if (compilationInformation.onErrorStatement != null) {
          for (LocalVariableSymbol local : compilationInformation.locals) {
            local.generateInitializer(m);
          }
        }

        // Need to copy reference parameters into their dereferenced temporary local variables
        for (LocalVariableSymbol param :compilationInformation.params) {
          // TODO: generate better code - only do this for used parameters
          param.readReferenceParameter(m);
        }

        Label startLabel = Method.newLabel();
        m.setLabel(startLabel);

        compilationInformation.statements.generate(m);

        m.setLabel(compilationInformation.exitLabel);

        // Need to write back local temps of reference parameters
        for (LocalVariableSymbol param :compilationInformation.params) {
          // TODO: generate better code - only do this for used parameters
          param.writeBackReferenceParameter(m);
        }

        if (compilationInformation.returnVariable == null) {
          m.generateInstrReturn();
        } else {
          compilationInformation.returnVariable.generateRead(m);
          compilationInformation.returnVariable.getType().generateReturn(m);
        }

        // Generate On Error statement (if there is one)
        if (compilationInformation.onErrorStatement != null) {

          Label handlerLabel = Method.newLabel();
          m.setLabel(handlerLabel);

          m.generateExceptionHandlerInfo(startLabel, compilationInformation.exitLabel, handlerLabel,
              "java/lang/Throwable");

          compilationInformation.onErrorStatement.generate(m);
        }

        // Generate local variable debug information
        if (compilationInformation.me != null) {
          generateLocalVarDebugInfo(m, compilationInformation.me);
        }
        for (LocalVariableSymbol param: compilationInformation.params) {
          generateLocalVarDebugInfo(m, param);
        }
        for (LocalVariableSymbol local: compilationInformation.locals) {
          generateLocalVarDebugInfo(m, local);
        }

        m.finishCodeGeneration();
      } catch (MethodTooBigException e) {
        compiler.error(Scanner.NO_POSITION, Error.errFunctionTooBig, getName(),
            definingObject.getType().internalName().replace('/', '.'));
      }
    }
  }

  /*
   * Generates debug information for a local variable.
   */
  private void generateLocalVarDebugInfo(Method m, LocalVariableSymbol local) {
    m.generateLocalVariableInformation(local.getStartScopeLabel(), local.getEndScopeLabel(),
        local.getName(), local.getType().signature(), local.getActualVarIndex());
  }

  // SymbolWithType implementation

  public final Type getType() {
    return type;
  }

  public final void setType(Type type) {
    this.type = type;
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.