All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.javascript.jscomp.DartSuperAccessorsPass Maven / Gradle / Ivy

/*
 * Copyright 2015 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 static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;

import com.google.javascript.jscomp.parsing.parser.FeatureSet;
import com.google.javascript.rhino.IR;
import com.google.javascript.rhino.Node;

/**
 * Converts {@code super} getter and setter calls in order to support the output
 * of the Dart Dev Compiler (https://github.com/dart-lang/dev_compiler). This has
 * to run before the {@link Es6ConvertSuper} pass.
 *
 * 

Note that the approach taken here doesn't lend itself to good optimizations of getters and * setters. An alternative approach is needed to generate fully optimizable output before general * ES6→ES5 lowering of super accessor expressions can be rolled out. * *

TODO(ochafik): Add support for `super.x++` (and --, pre/post variants). * * @author [email protected] (Olivier Chafik) */ public final class DartSuperAccessorsPass implements NodeTraversal.Callback, HotSwapCompilerPass { static final String CALL_SUPER_GET = "$jscomp.superGet"; static final String CALL_SUPER_SET = "$jscomp.superSet"; private final AbstractCompiler compiler; /** * Whether JSCompiler_renameProperty can and should be used (i.e. if we think a RenameProperties * pass will be run). */ private final boolean renameProperties; public DartSuperAccessorsPass(AbstractCompiler compiler) { this.compiler = compiler; CompilerOptions options = compiler.getOptions(); this.renameProperties = options.propertyRenaming == PropertyRenamingPolicy.ALL_UNQUOTED; checkState( options.getOutputFeatureSet().contains(FeatureSet.ES5), "Dart super accessors pass requires ES5+ output"); // We currently rely on JSCompiler_renameProperty, which is not type-aware. // We would need something like goog.reflect.object (with the super class type), // but right now this would yield much larger code. checkState( !options.shouldAmbiguateProperties() && !options.shouldDisambiguateProperties(), "Dart super accessors pass is not compatible with property (dis)ambiguation yet"); } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (isSuperGet(n)) { visitSuperGet(n); return false; } else if (isSuperSet(n)) { if (!n.isAssign()) { n = normalizeAssignmentOp(n); } visitSuperSet(n); return false; } return true; } private static boolean isCalled(Node n) { Node parent = n.getParent(); return parent.isCall() && (n == parent.getFirstChild()); } /** Transforms `a += b` to `a = a + b`. */ private static Node normalizeAssignmentOp(Node n) { Node lhs = n.getFirstChild(); Node rhs = n.getLastChild(); Node newRhs = new Node(NodeUtil.getOpFromAssignmentOp(n), lhs.cloneTree(), rhs.cloneTree()).srcrefTree(n); return replace(n, IR.assign(lhs.cloneTree(), newRhs).srcrefTree(n)); } @Override public void visit(NodeTraversal t, Node n, Node parent) {} private boolean isSuperGet(Node n) { return (n.isGetProp() || n.isGetElem()) && !isCalled(n) && n.getFirstChild().isSuper() && isInsideInstanceMember(n); } private boolean isSuperSet(Node n) { return NodeUtil.isAssignmentOp(n) && isSuperGet(n.getFirstChild()); } /** * Returns true if this node is or is enclosed by an instance member definition * (non-static method, getter or setter). */ private static boolean isInsideInstanceMember(Node n) { while (n != null) { if (n.isMemberFunctionDef() || n.isGetterDef() || n.isSetterDef() || n.isComputedProp()) { return !n.isStaticMember(); } if (n.isClass()) { // Stop at the first enclosing class. return false; } n = n.getParent(); } return false; } private void visitSuperGet(Node superGet) { Node name = superGet.getLastChild().cloneTree(); Node callSuperGet = IR.call( NodeUtil.newQName(compiler, CALL_SUPER_GET).srcrefTree(superGet), IR.thisNode().srcref(superGet), superGet.isGetProp() ? renameProperty(name) : name); replace(superGet, callSuperGet); compiler.ensureLibraryInjected("es6_dart_runtime", false); compiler.reportChangeToEnclosingScope(callSuperGet); } private void visitSuperSet(Node superSet) { checkArgument(superSet.isAssign()); // First, recurse on the assignment's right-hand-side. NodeTraversal.traverse(compiler, superSet.getLastChild(), this); Node rhs = superSet.getLastChild(); Node superGet = superSet.getFirstChild(); Node name = superGet.getLastChild().cloneTree(); Node callSuperSet = IR.call( NodeUtil.newQName(compiler, CALL_SUPER_SET).srcrefTree(superSet), IR.thisNode().srcref(superSet), superGet.isGetProp() ? renameProperty(name) : name, rhs.cloneTree()); replace(superSet, callSuperSet); compiler.ensureLibraryInjected("es6_dart_runtime", false); compiler.reportChangeToEnclosingScope(callSuperSet); } private static Node replace(Node original, Node replacement) { original.replaceWith(replacement); return replacement; } /** * Wraps a property string in a JSCompiler_renameProperty call. * *

Should only be called in phases running before {@link RenameProperties}, * if such a pass is even used (see {@link #renameProperties}). */ private Node renameProperty(Node propertyName) { checkArgument(propertyName.isString()); if (!renameProperties) { return propertyName; } Node call = IR.call( IR.name(NodeUtil.JSC_PROPERTY_NAME_FN).srcref(propertyName), propertyName); call.srcref(propertyName); call.putBooleanProp(Node.FREE_CALL, true); call.putBooleanProp(Node.IS_CONSTANT_NAME, true); return call; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, externs, this); NodeTraversal.traverse(compiler, root, this); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { NodeTraversal.traverse(compiler, scriptRoot, this); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy