@Override public void visitCallSite(NodeTraversal t, Node callNode, FunctionState fs) { Preconditions.checkState(fs.hasExistingFunctionDefinition()); if (fs.canInline()) { Reference ref = fs.getReference(callNode); // There are two cases ref can be null: if the call site was introduced // because it was part of a function that was inlined during this pass // or if the call site was trimmed from the list of references because // the function couldn't be inlined at this location. if (ref != null) { if (specializationState != null) { Node containingFunction = getContainingFunction(t); if (containingFunction != null) { // Report that the function was specialized so that // {@link SpecializeModule} can fix it up. specializationState.reportSpecializedFunction(containingFunction); } } inlineFunction(t, ref, fs); // Keep track of references that have been inlined so that // we can verify that none have been missed. ref.inlined = true; } } }
private boolean maybeAddReferenceUsingMode( NodeTraversal t, FunctionState fs, Node callNode, JSModule module, InliningMode mode) { // If many functions are inlined into the same function F in the same // inlining round, then the size of F may exceed the max size. // This could be avoided if we bail later, during the inlining phase, eg, // in Inline#visitCallSite. However, that is not safe, because at that // point expression decomposition has already run, and we want to // decompose expressions only for the calls that are actually inlined. if (enforceMaxSizeAfterInlining && targetSizeAfterInlineExceedsLimit(t, fs)) { return false; } Reference candidate = new Reference(callNode, t.getScope(), module, mode); CanInlineResult result = injector.canInlineReferenceToFunction( candidate, fs.getFn().getFunctionNode(), fs.getNamesToAlias(), fs.getReferencesThis(), fs.hasInnerFunctions()); if (result != CanInlineResult.NO) { // Yeah! candidate.setRequiresDecomposition(result == CanInlineResult.AFTER_PREPARATION); fs.addReference(candidate); return true; } return false; }
if (maybeDecl.isVarDeclaration()) { Preconditions.checkState(!maybeDecl.isInitializingDeclaration()); Reference maybeInit = references.get(index); if (maybeInit.isSimpleAssignmentToName()) { return true; } }
/** @return Whether the variable is never assigned a value. */ boolean isNeverAssigned() { int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { return false; } } return true; }
/** * @param index The index into the references array to look for an assigning declaration. * <p>This is either the declaration if a value is assigned (such as "var a = 2", "function * a()...", "... catch (a)..."). */ private boolean isInitializingDeclarationAt(int index) { Reference maybeInit = references.get(index); if (maybeInit.isInitializingDeclaration()) { // This is a declaration that represents the initial value. // Specifically, var declarations without assignments such as "var a;" // are not. return true; } return false; }
/** * @param index The index into the references array to look for an initialized assignment * reference. That is, an assignment immediately follow a variable declaration that itself * does not initialize the variable. */ private boolean isInitializingAssignmentAt(int index) { if (index < references.size() && index > 0) { Reference maybeDecl = references.get(index - 1); if (maybeDecl.isVarDeclaration()) { Preconditions.checkState(!maybeDecl.isInitializingDeclaration()); Reference maybeInit = references.get(index); if (maybeInit.isSimpleAssignmentToName()) { return true; } } } return false; }
/** @return The one and only assignment. Returns if there are 0 or 2+ assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { if (assignment == null) { assignment = ref; } else { return null; } } } return assignment; }
/** @return Whether the variable is only assigned a value once for its lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. for (BasicBlock block = ref.getBasicBlock(); block != null; block = block.getParent()) { if (block.isFunction) { if (ref.getSymbol().getScope() != ref.scope) { return false; } break; } else if (block.isLoop) { return false; } } return true; }
/** * Determines if the variable for this reference collection is "well-defined." A variable is * well-defined if we can prove at compile-time that it's assigned a value before it's used. * * <p>Notice that if this function returns false, this doesn't imply that the variable is used * before it's assigned. It just means that we don't have enough information to make a * definitive judgment. */ protected boolean isWellDefined() { int size = references.size(); if (size == 0) { return false; } // If this is a declaration that does not instantiate the variable, // it's not well-defined. Reference init = getInitializingReference(); if (init == null) { return false; } Preconditions.checkState(references.get(0).isDeclaration()); BasicBlock initBlock = init.getBasicBlock(); for (int i = 1; i < size; i++) { if (!initBlock.provablyExecutesBefore(references.get(i).getBasicBlock())) { return false; } } return true; }