Package com.google.javascript.jscomp

Source Code of com.google.javascript.jscomp.Es6ToEs3ClassSideInheritance$FindStaticMembers

/*
* Copyright 2014 The Closure Compiler Authors.
*
* 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.javascript.jscomp;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback;
import com.google.javascript.jscomp.NodeTraversal.AbstractPreOrderCallback;
import com.google.javascript.rhino.IR;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;

import java.util.HashSet;
import java.util.Set;

/**
* Rewrites static inheritance to explicitly copy inherited properties from super class to
* sub class eliminating dynamic copying from the original transpilation pass in the process.
*
* For example, {@link Es6ToEs3Converter} will convert
*
* <pre>
* class Foo { static f() {} }
* class Bar extends Foo {}
* </pre>
* to
*
* <pre>
* function Foo() {}
* Foo.f = function() {};
* function Bar() {}
* $jscomp$copy$properties(Foo, Bar);
* </pre>
*
* and then this class will convert that to
*
* <pre>
* function Foo() {}
* Foo.f = function() {};
* function Bar() {}
* Bar.f = Foo.f;
* </pre>
*
* @author mattloring@google.com (Matthew Loring)
*/
public class Es6ToEs3ClassSideInheritance extends AbstractPostOrderCallback
    implements CompilerPass {

  final AbstractCompiler compiler;
  // Map from class names to the static members in each class.
  private final Multimap<String, String> staticMembers = ArrayListMultimap.create();

  static final DiagnosticType DUPLICATE_CLASS = DiagnosticType.error(
      "DUPLICATE_CLASS",
      "Multiple classes cannot share the same name.");

  private final Set<String> multiplyDefinedClasses = new HashSet<>();

  public Es6ToEs3ClassSideInheritance(AbstractCompiler compiler) {
    this.compiler = compiler;
  }

  @Override
  public void process(Node externs, Node root) {
    FindCopyProp findCopyProp = new FindCopyProp();
    NodeTraversal.traverse(compiler, root, findCopyProp);
    if (findCopyProp.found) {
      NodeTraversal.traverse(compiler, root, new FindStaticMembers());
      NodeTraversal.traverse(compiler, root, this);
    }
  }

  @Override
  public void visit(NodeTraversal t, Node n, Node parent) {
    if (!n.isCall()) {
      return;
    }
    if (n.getFirstChild().matchesQualifiedName(Es6ToEs3Converter.COPY_PROP)) {
      Node superClassName = n.getLastChild();
      Node subClassName = n.getChildBefore(superClassName);
      String key = superClassName.getQualifiedName();
      if (multiplyDefinedClasses.contains(key)) {
        compiler.report(JSError.make(n, DUPLICATE_CLASS));
        return;
      }
      if (staticMembers.containsKey(key)) {
        for (String staticMember : staticMembers.get(key)) {
          Node sAssign = IR.exprResult(
              IR.assign(
                  IR.getprop(subClassName.cloneTree(), IR.string(staticMember)),
                  IR.getprop(superClassName.cloneTree(), IR.string(staticMember))));
          sAssign.useSourceInfoIfMissingFromForTree(n);
          parent.getParent().addChildAfter(sAssign, parent);
          staticMembers.put(subClassName.getQualifiedName(),
              staticMember);
        }
        parent.detachFromParent();
      }
    }
  }

  private class FindCopyProp extends AbstractPreOrderCallback {

    private boolean found = false;

    @Override
    public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
      if (found || n.isCall()
          && n.getFirstChild().matchesQualifiedName(Es6ToEs3Converter.COPY_PROP)) {
        found = true;
        return false;
      }
      return true;
    }
  }

  private class FindStaticMembers extends NodeTraversal.AbstractPostOrderCallback {

    private final Set<String> classNames = new HashSet<String>();

    @Override
    public void visit(NodeTraversal nodeTraversal, Node n, Node parent) {
      switch(n.getType()) {
        case Token.VAR:
          visitVar(n);
          break;
        case Token.ASSIGN:
          visitAssign(n);
          break;
        case Token.FUNCTION:
          visitFunctionClassDef(n);
          break;
      }
    }

    private void visitFunctionClassDef(Node n) {
      JSDocInfo classInfo = NodeUtil.getBestJSDocInfo(n);
      if (classInfo != null && classInfo.isConstructor()) {
        String name = NodeUtil.getFunctionName(n);
        if (classNames.contains(name)) {
          multiplyDefinedClasses.add(name);
        } else if (name != null) {
          classNames.add(name);
        }
      }
    }

    private void visitAssign(Node n) {
      // Alias for classes. We assume that the alias appears after the class
      // declaration.
      if (classNames.contains(n.getLastChild().getQualifiedName())) {
        String maybeAlias = n.getFirstChild().getQualifiedName();
        if (maybeAlias != null) {
          classNames.add(maybeAlias);
          staticMembers.putAll(maybeAlias,
              staticMembers.get(n.getLastChild().getQualifiedName()));
        }
      } else if (n.getFirstChild().isGetProp()) {
        Node getProp = n.getFirstChild();
        String maybeClassName = getProp.getFirstChild().getQualifiedName();
        if (classNames.contains(maybeClassName)) {
          staticMembers.put(maybeClassName, getProp.getLastChild().getString());
        }
      }
    }

    private void visitVar(Node n) {
      Node child = n.getFirstChild();
      if (!child.hasChildren()) {
        return;
      }
      String maybeOriginalName = child.getFirstChild().getQualifiedName();
      if (classNames.contains(maybeOriginalName)) {
        String maybeAlias = child.getQualifiedName();
        if (maybeAlias != null) {
          classNames.add(maybeAlias);
          staticMembers.putAll(maybeAlias, staticMembers.get(maybeOriginalName));
        }
      }
    }
  }
}
TOP

Related Classes of com.google.javascript.jscomp.Es6ToEs3ClassSideInheritance$FindStaticMembers

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.