Package flex2.compiler.as3.genext

Source Code of flex2.compiler.as3.genext.GenerativeSecondPassEvaluator

/*
*
*  Licensed to the Apache Software Foundation (ASF) under one or more
*  contributor license agreements.  See the NOTICE file distributed with
*  this work for additional information regarding copyright ownership.
*  The ASF licenses this file to You 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 flex2.compiler.as3.genext;

import flex2.compiler.CompilationUnit;
import flex2.compiler.util.CompilerMessage;
import flex2.compiler.as3.As3Compiler;
import flex2.compiler.as3.binding.ClassInfo;
import flex2.compiler.as3.binding.InterfaceInfo;
import flex2.compiler.as3.binding.TypeAnalyzer;
import flex2.compiler.as3.reflect.NodeMagic;
import flex2.compiler.mxml.lang.StandardDefs;
import flex2.compiler.util.*;
import macromedia.asc.parser.*;
import macromedia.asc.util.Context;
import macromedia.asc.util.ObjectList;
import flash.swf.tools.as3.EvaluatorAdapter;
import flash.util.FileUtils;
import flash.util.Trace;
import org.apache.flex.forks.velocity.Template;
import org.apache.flex.forks.velocity.VelocityContext;

import java.io.*;
import java.util.*;

/**
* A common base class for Bindable and Managed metadata second pass
* evaluators.
*
* @author Basil Hosmer
* @author Paul Reilly
*/
public abstract class GenerativeSecondPassEvaluator extends EvaluatorAdapter
{
  protected final CompilationUnit unit;
  protected final StandardDefs standardDefs;
  protected final String generatedOutputDirectory;
  protected final Map<String, ? extends GenerativeClassInfo> classMap;
  protected final Set<ClassDefinitionNode> evaluatedClasses;
  protected final TypeAnalyzer typeAnalyzer;
  protected final MetaDataEvaluator metaDataEvaluator;
    protected final boolean generateAbstractSyntaxTree;
    protected final boolean processComments;

  /**
   *
   */
  public GenerativeSecondPassEvaluator(CompilationUnit unit,
                     Map<String, ? extends GenerativeClassInfo> classMap,
                     TypeAnalyzer typeAnalyzer,
                     String generatedOutputDirectory,
                                         boolean generateAbstractSyntaxTree, boolean processComments)
  {
    this.unit = unit;
    this.standardDefs = unit.getStandardDefs();
    this.classMap = classMap;
    this.typeAnalyzer = typeAnalyzer;
    this.generatedOutputDirectory = generatedOutputDirectory;
        this.generateAbstractSyntaxTree = generateAbstractSyntaxTree;
        this.processComments = processComments;
       
    evaluatedClasses = new HashSet<ClassDefinitionNode>();
    setLocalizationManager(ThreadLocalToolkit.getLocalizationManager());

    metaDataEvaluator = new MetaDataEvaluator();
  }

  protected boolean checkForExistingMethods(Context context, ClassDefinitionNode node,
                                              ClassInfo classInfo, InterfaceInfo interfaceInfo)
  {
    // NOTE: here we want to log as many errors as possible before returning
    // this is why we don't bail after finding the first error
    boolean result = false;

        List functionNames = interfaceInfo.getFunctionNames();

        if (functionNames != null)
        {
            Iterator iterator = functionNames.iterator();
       
            while ( iterator.hasNext() )
            {
                QName qName = (QName) iterator.next();
                String functionName = qName.getLocalPart();

                if (!functionName.equals("$construct") &&
                    classInfo.definesFunction(functionName, true))
                {
                    context.localizedError2(node.pos(),
                                            new ExistingMethodError(interfaceInfo.getInterfaceName(),
                                                                    node.name.name,
                                                                    functionName));
                    result = true;
                }
            }
        }

    return result;
  }

  /**
   * register a renamed variable into our unit's context
   */
    protected void registerRenamedAccessor(GenerativeClassInfo.AccessorInfo info)
  {
        @SuppressWarnings("unchecked")
    Map<String, String> renamedVariableMap = (Map<String, String>) unit.getContext().getAttribute(flex2.compiler.CompilerContext.RENAMED_VARIABLE_MAP);
       
    if (renamedVariableMap == null)
    {
      renamedVariableMap = new HashMap<String, String>();
      unit.getContext().setAttribute(flex2.compiler.CompilerContext.RENAMED_VARIABLE_MAP, renamedVariableMap);
    }
    renamedVariableMap.put(info.getBackingPropertyName(), info.getPropertyName());
  }

  /**
   * Mangle a variable's name and make it private.
   */
  protected static void hideVariable(VariableDefinitionNode variableDefinition, GenerativeClassInfo.AccessorInfo info)
  {
    VariableBindingNode variableBindingNode = NodeMagic.getVariableBinding(variableDefinition);

    NodeMagic.setVariableBindingName(variableBindingNode, info.getBackingPropertyName());

    makeAttrListPrivate(variableDefinition);
  }

  /**
   * Mangle a function's name and make it private.
   */
  protected static void hideFunction(FunctionDefinitionNode functionDefinition, GenerativeClassInfo.AccessorInfo info)
  {
    NodeMagic.prefixFunctionName(functionDefinition, info.getBackingPrefix());

    makeAttrListPrivate(functionDefinition);
  }

  protected static boolean isIdentifier(IdentifierNode identifier, String attribute)
  {
    boolean result = false;
    if (identifier != null)
      result = identifier.name.equals(attribute);

    return result;
  }

  private static void makeAttrListPrivate(DefinitionNode def)
  {
    // we have to remove the override should it exist as it isn't legal to
    // override private members.
    if (def.attrs != null)
    {
      Iterator iterator = def.attrs.items.iterator();

      while ( iterator.hasNext() )
      {
        Object node = iterator.next();
        IdentifierNode identifier = null;
        if (node instanceof MemberExpressionNode)
        {
          // if this identifier is an override we need to remove it
          // other wise we can go ahead and mark it private.
          identifier = NodeMagic.getIdentifier((MemberExpressionNode) node);
          if (isIdentifier(identifier, NodeMagic.OVERRIDE))
          {
            iterator.remove();
          }
          else
          {
            ensureNonPublic(identifier);
          }
        }
        else if (node instanceof ListNode)
        {
          ListNode list = (ListNode) node;

          Iterator listIterator = list.items.iterator();

          while ( listIterator.hasNext() )
          {
            Object listNode = listIterator.next();

            if (listNode instanceof MemberExpressionNode)
            {
              // if this identifier is an override we need to remove it
              // other wise we can go ahead and mark it private.
              // NOTE: it appears that override _always_ lives in this
              // portion of the node tree, i.e. the ListNode part
              identifier = NodeMagic.getIdentifier((MemberExpressionNode) listNode);
              if (isIdentifier(identifier, NodeMagic.OVERRIDE))
              {
                iterator.remove();
              }
              else
              {
                ensureNonPublic(identifier);
              }
            }
          }
        }
      }
    }
  }

  /**
   * NOTE: used to be called 'makePrivate'; changed name to something closer to what it actually does.
   * If what it really needs to do is make everything private, it needs a rewrite. Old comment follows:
   * TODO what about internals, don't we want to make them private also?
   */
  protected static void ensureNonPublic(IdentifierNode identifier)
  {
    if (isIdentifier(identifier, NodeMagic.PUBLIC))
    {
      identifier.name = NodeMagic.PRIVATE;
    }
  }

  /**
   *
   */
  protected abstract String getTemplateName();

  /**
   * Used to allow subclasses with different template locations to specify
   * where those locations are.
   * @return template path
   */
  protected abstract String getTemplatePath();

  /**
   *
   */
  protected abstract Map getTemplateVars();

  /**
   *
   */
  protected abstract String getGeneratedSuffix();

  /**
   * Produces a parsed ProgramNode containing a single ClassDefinitionNode containing generated wrappers for
   * [Bindable] properties.
   *
   * This codegen happens after the original properties have been prepped (i.e., made private, name-mangled, etc.),
   * and is based on information exposed to the template as a collection of info structures (see implementations of
   * getTemplateVars()).
   */
  protected ProgramNode generateSupportCode(Context context, String className)
  {
    ProgramNode programNode = null;

    String templateName = getTemplateName();
    Map templateVars = getTemplateVars();
    String suffix = getGeneratedSuffix();

    Template template = null;
    try
    {
      template = VelocityManager.getTemplate(getTemplatePath() + templateName);
    }
    catch(Exception e) {}


    if (template != null)
    {
      try
      {
        StringWriter stringWriter = new StringWriter();

        VelocityContext velocityContext = new VelocityContext();

        for (Iterator iter = templateVars.entrySet().iterator(); iter.hasNext(); )
        {
          Map.Entry entry = (Map.Entry)iter.next();
          velocityContext.put((String)entry.getKey(), entry.getValue());
        }

        template.merge(velocityContext, stringWriter);

        String sourceName = unit.getSource().getName();
        String prefix = sourceName.substring(0, sourceName.lastIndexOf(File.separatorChar) + 1);
        String generatedName = prefix + className + suffix;

        // convention: prepend underscore to fake code snippets that are written out just for debugging?
        if (generatedOutputDirectory != null)
        {
          generatedName = FileUtils.addPathComponents( generatedOutputDirectory, "_" + className + suffix, File.separatorChar );
        }

        if (generatedOutputDirectory != null)
        {
          BufferedWriter fileWriter = null;

          try
          {
            fileWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(generatedName), "UTF-8"));
            fileWriter.write( stringWriter.toString() );
            fileWriter.flush();
          }
          catch (IOException ioException)
          {
            ioException.printStackTrace();
          }
          finally
          {
            if (fileWriter != null)
            {
              try
              {
                fileWriter.close();
              }
              catch (IOException ex)
              {
              }
            }
          }
        }

        context = new Context(context.statics);
        context.setPath("");
        context.setScriptName(generatedName);
        context.setHandler(new flex2.compiler.as3.As3Compiler.CompilerHandler());
        context.statics.handler = context.getHandler();

        Parser parser = new Parser(context, stringWriter.toString(), generatedName, processComments, false);

        programNode = parser.parseProgram();

                As3Compiler.cleanNodeFactory(context.getNodeFactory());
      }
      catch (Exception e)
      {
        ThreadLocalToolkit.log(new TemplateRunException(templateName, className, e.getLocalizedMessage()));

        if (Trace.error)
        {
          e.printStackTrace();
        }
      }
    }
    else
    {
      ThreadLocalToolkit.log(new TemplateLoadError(templateName));
    }

    return programNode;
  }

  /**
   * Modifies a ClassDefinitionNode by a) generating a temp class full of property wrappers etc., as directed by info
   * structure, and b) splicing these generated wrappers into the original classdef.
   */
  protected void modifySyntaxTree(Context context, ClassDefinitionNode classDef, GenerativeClassInfo classInfo)
  {
        ProgramNode genProgramNode = generateSupportCode(context, classDef.name.name);

        if (genProgramNode.statements.items != null)
        {
            applyGeneratedSupportCode(context, classDef, classInfo, genProgramNode);
        }
  }

  /**
   * Apply contents of generated class to our original classdef.
   */
  private void applyGeneratedSupportCode(Context context,
                       ClassDefinitionNode classDef,
                       GenerativeClassInfo classInfo,
                       ProgramNode genProgramNode)
  {
    for (Iterator genIter = genProgramNode.statements.items.iterator(); genIter.hasNext(); )
    {
      Object genItem = genIter.next();
      if (genItem instanceof ClassDefinitionNode)
      {
        ClassDefinitionNode genClassDef = (ClassDefinitionNode) genItem;
        if (genClassDef.statements != null)
        {
          for (Iterator genClassStmtIter = genClassDef.statements.items.iterator(); genClassStmtIter.hasNext(); )
          {
            Node genClassStmt = (Node) genClassStmtIter.next();

            /**
             * type-specific node prep
             */
            if (genClassStmt instanceof MetaDataNode)
            {
              prepMetaDataNode(context, (MetaDataNode) genClassStmt);
            }
            else if (genClassStmt instanceof FunctionDefinitionNode)
            {
              prepFunctionDefinitionNode(context, classDef, classInfo, (FunctionDefinitionNode) genClassStmt);
            }
            else if (genClassStmt instanceof VariableDefinitionNode)
            {
              VariableDefinitionNode variableDefinition = (VariableDefinitionNode) genClassStmt;
              prepVariableDefinitionNode(classDef, variableDefinition);
                            // If we get here, the genClassStmt is assumed to be the
                            // _bindingEventDispatcher variable or one of the variables
                            // from ManagedProperty.vm.  Set the position to -1, so it
                            // won't interfere with debugging.
              resetPositions(context, variableDefinition, -1);
            }

            addGeneratedMember(classDef, genClassStmt);
          }

          applyGeneratedInterfaces(classInfo, classDef, genClassDef);
        }
      }
    }
  }

  /**
   * add generated statement to original class
   */
  private void addGeneratedMember(ClassDefinitionNode classDef, Node genClassStmt)
  {
    if (!(genClassStmt instanceof NamespaceDefinitionNode))
    {
      classDef.statements.items.add(genClassStmt);
    }
  }

  /**
   * prepare MetaDataNode for transfer from generated class to original class.
   * Generated metadata will be e.g. [Bindable("propertyChange")]
   */
  protected void prepMetaDataNode(Context context, MetaDataNode metaData)
  {
    //  transfer generated (in-class) metadata to unit's all-the-metadata list
    metaData.evaluate(context, metaDataEvaluator);
    unit.metadata.add(metaData);
  }

  /**
   * prepare FunctionDefinitionNode for transfer from generated class to original class.
   * Generated functions that are getter/setter wrappers corresponding to original variables or getter/setters, as
   * indicated in classInfo, need position info and metadata to be patched over from original properties.
   */
  private void prepFunctionDefinitionNode(Context context,
                      ClassDefinitionNode classDef,
                      GenerativeClassInfo classInfo,
                      FunctionDefinitionNode genFuncDef)
  {
    String functionName = NodeMagic.getFunctionName(genFuncDef);

    //  patch original classdef's node stuff into generated function def node
    genFuncDef.cx = classDef.cx;
    genFuncDef.fexpr.cx = classDef.cx;
    genFuncDef.pkgdef = classDef.pkgdef;
    genFuncDef.skipNode(true);

    //  TODO why would either of these be null?
    if ((functionName != null) && (classInfo.getAccessors() != null))
    {
      GenerativeClassInfo.AccessorInfo accessorInfo = classInfo.getAccessor(functionName);

      if (accessorInfo != null)
      {
        if (accessorInfo instanceof GenerativeClassInfo.VariableInfo)
        {
          //  original property was a variable
          GenerativeClassInfo.VariableInfo variableInfo = (GenerativeClassInfo.VariableInfo)accessorInfo;

          //  patch original position info into generated node
          resetPositions(context, genFuncDef, variableInfo.getPosition());

          //  patch (only) generated getter with original metadata from var
          if (NodeMagic.functionIsGetter(genFuncDef))
          {
            patchMetaData(genFuncDef, variableInfo.getMetaData());
          }
        }
        else if (accessorInfo instanceof GenerativeClassInfo.GetterSetterInfo)
        {
          //  original property was a getter/setter pair
          GenerativeClassInfo.GetterSetterInfo getterSetterInfo = (GenerativeClassInfo.GetterSetterInfo)accessorInfo;

          //  patch original position info and metadata into generated node
          if (NodeMagic.functionIsGetter(genFuncDef))
          {
            resetPositions(context, genFuncDef, getterSetterInfo.getGetterPosition());
            patchMetaData(genFuncDef, getterSetterInfo.getGetterMetaData());
          }
          else if (NodeMagic.functionIsSetter(genFuncDef))
          {
            resetPositions(context, genFuncDef, getterSetterInfo.getSetterPosition());
            patchMetaData(genFuncDef, getterSetterInfo.getSetterMetaData());
          }
        }

        classInfo.removeOriginalMetaData( accessorInfo.getDefinitionNode() );
      }
      else
      {
                // If we get here, the genFuncDef is assumed to be one of the the
                // IEventDispatcher implementation functions (addEventListener(),
                // dispatchEvent(), hasEventListener(), removeEventListener(), or
                // willTrigger()) or one of the functions defined in ManagedProperty.vm.
                // Set the position to -1, so they won't interfere with debugging.
        resetPositions(context, genFuncDef, -1);
      }
    }
  }

  /**
   *
   */
  private void patchMetaData(DefinitionNode def, List metadata)
  {
    for (Iterator it = metadata.iterator(); it.hasNext();)
    {
      MetaDataNode md = (MetaDataNode) it.next();
      if (md.getId() != null && md.getId().equals( StandardDefs.MD_BINDABLE ) && ((md.getValues() == null) || (md.getValues().length == 0)))
        continue;
     
      if (md instanceof DocCommentNode)
      {
        md.def = def;
      }

      def.addMetaDataNode( md );
    }
  }

  /**
   *
   */
  private void resetPositions(Context context, Node node, int position)
  {
    PositionResetEvaluator positionResetEvaluator = new PositionResetEvaluator(position);
    node.evaluate(context, positionResetEvaluator);
  }

  /**
   * prepare VariableDefinitionNode for transfer from generated class to original class.
   * Generated variables will be miscellaneous, e.g. EventDispatcher delegate
   */
  private void prepVariableDefinitionNode(ClassDefinitionNode classDef, VariableDefinitionNode variableDefinition)
  {
    //  patch original classdef's node stuff into generated variable def node
    variableDefinition.cx = classDef.cx;
    variableDefinition.pkgdef = classDef.pkgdef;
    variableDefinition.skipNode(true);
  }

  /**
   * generated class may have interfaces that may be missing from original
   */
  private void applyGeneratedInterfaces(GenerativeClassInfo classInfo, ClassDefinitionNode origClassDef, ClassDefinitionNode genClassDef)
  {
    if (classInfo.needsAdditionalInterfaces())
    {
      if (origClassDef.interfaces == null)
      {
        origClassDef.interfaces = genClassDef.interfaces;
      }
      else
      {
        if (genClassDef.interfaces != null)
        {
          ObjectList interfaces = genClassDef.interfaces.items;
          for (int i = 0; i < interfaces.size(); i++)
          {
            origClassDef.interfaces.items.add(genClassDef.interfaces.items.get(i));
          }
        }
      }
    }
  }

  /**
   * This is called *after* TypeAnalyzer has collected reflection info on our class, but *before* the second pass
   * over the syntax tree and subsequent code generation. It gives us a chance to do grooming/verification of
   * generative stuff collected in the first pass, using reflection info. Currently that includes the following:
   *
   * <li>- strip non-read-write getter/setters from generative info
   */
  protected void postProcessClassInfo(Context context, GenerativeClassInfo classInfo)
  {
    // [Bindable]/[Managed] getter/setters must be read-write, i.e. both getter and setter must be defined on the
    // class. It's overly complicated to verify this inline on the first pass, so here we simply verify that both
    // are present in the class sig.
    // If not, we remove them from the GenerativeClassInfo structure, so code won't be generated for them.
    // Also, if a non-read-write accessor has been defined explicitly (i.e. at property level), emit a warning.
    if (classInfo.getAccessors() != null)
    {
      for (Iterator iter = classInfo.getAccessors().entrySet().iterator(); iter.hasNext(); )
      {
        Map.Entry entry = (Map.Entry)iter.next();
        GenerativeClassInfo.AccessorInfo info = (GenerativeClassInfo.AccessorInfo)entry.getValue();

        if (info.getIsFunction())
        {
          String propName = info.getPropertyName();
          ClassInfo reflectionInfo = classInfo.getClassInfo();

          if (!reflectionInfo.definesGetter(propName, true))
          {
            if (info.getIsExplicit())
            {
              context.localizedWarning2(((GenerativeClassInfo.GetterSetterInfo)info).getSetterPosition(), new BindableOnWriteOnlySetter());
            }
            iter.remove();
          }
          else if (!reflectionInfo.definesSetter(propName, true))
          {
            if (info.getIsExplicit())
            {
              context.localizedWarning2(((GenerativeClassInfo.GetterSetterInfo)info).getGetterPosition(), new BindableOnReadOnlyGetter());
            }
            iter.remove();
          }
        }
      }
    }
  }

  /**
   * CompilerMessages
   */
  public static class BindableOnReadOnlyGetter extends CompilerMessage.CompilerWarning {

        private static final long serialVersionUID = -7879513353111728085L;}
  public static class BindableOnWriteOnlySetter extends CompilerMessage.CompilerWarning {

        private static final long serialVersionUID = 5285045188791977806L;}

  /**
   * CompilerMessages
   */
  public static class ExistingMethodError extends CompilerMessage.CompilerError
  {
    private static final long serialVersionUID = 3591535632396036477L;
        public String interfaceName, className, methodName;
    public ExistingMethodError(String interfaceName, String className, String methodName)
    {
      this.interfaceName = interfaceName;
      this.className = className;
      this.methodName = methodName;
    }
  }

  public static class TemplateLoadError extends CompilerMessage.CompilerError
  {
    private static final long serialVersionUID = -4500622167729742558L;
        public String templateName;
    public TemplateLoadError(String templateName) { this.templateName = templateName; }
  }

  public static class TemplateRunException extends CompilerMessage.CompilerError
  {
    private static final long serialVersionUID = -6388819951389806673L;
        public String templateName, className, exceptionText;
    public TemplateRunException(String templateName, String className, String exceptionText)
    {
      this.templateName = templateName;
      this.className = className;
      this.exceptionText = exceptionText;
      noPath();
    }
  }
}
TOP

Related Classes of flex2.compiler.as3.genext.GenerativeSecondPassEvaluator

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.