public <D extends Definition> CompilingDef<D> getCompiling(DefDescriptor<D> descriptor) { @SuppressWarnings("unchecked") CompilingDef<D> cd = (CompilingDef<D>) compiled.get(descriptor); if (cd == null) { cd = new CompilingDef<D>(); compiled.put(descriptor, cd); } cd.descriptor = descriptor; return cd; }
/** * Typesafe helper for getDef. * * <p>This adds new definitions (unvalidated) to the list passed in. Definitions that were * previously built are simply added to the local cache. * * <p>The quick fix exception case is actually a race condition where we previously had a set of * depenendencies, and something changed, making our set inconsistent. There are no guarantees * that during a change all MDRs will have a correct set of definitions. * * @param context The aura context for the compiling def. * @param descriptor the descriptor for which we need a definition. * @return A compilingDef for the definition, or null if not needed. * @throws QuickFixException if something has gone terribly wrong. */ private <D extends Definition> void validateHelper( AuraContext context, DefDescriptor<D> descriptor, List<CompilingDef<?>> compiled) throws QuickFixException { CompilingDef<D> compiling = new CompilingDef<D>(); compiling.descriptor = descriptor; if (!fillCompilingDef(compiling, context)) { throw new DefinitionNotFoundException(descriptor); } if (compiling.built) { compiled.add(compiling); } else { defs.put(compiling.descriptor, compiling.def); } }
/** * A private helper routine to make the compiler code more sane. * * <p>This processes a single definition in a dependency tree. It works as a single step in a * breadth first traversal of the tree, accumulating children in the 'deps' set, and updating the * compile context with the current definition. * * <p>Note that once the definition has been retrieved, this code uses the 'canonical' descriptor * from the definition, discarding the incoming descriptor. * * @param descriptor the descriptor that we are currently handling, must not be in the compiling * defs. * @param cc the compile context to allow us to accumulate information. * @param deps the set of dependencies that we are accumulating. * @throws QuickFixException if the definition is not found, or validateDefinition() throws one. */ private <D extends Definition> D getHelper( DefDescriptor<D> descriptor, CompileContext cc, Set<DefDescriptor<?>> deps) throws QuickFixException { CompilingDef<D> cd = cc.getCompiling(descriptor); if (cd.def != null) { return cd.def; } try { if (!fillCompilingDef(cd, cc.context)) { // // At this point, we have failed to get the def, so we should throw an // error. The first stanza is to provide a more useful error description // including the set of components using the missing component. // if (!cd.parents.isEmpty()) { StringBuilder sb = new StringBuilder(); Location handy = null; for (Definition parent : cd.parents) { handy = parent.getLocation(); if (sb.length() != 0) { sb.append(", "); } sb.append(parent.getDescriptor().toString()); } throw new DefinitionNotFoundException(descriptor, handy, sb.toString()); } throw new DefinitionNotFoundException(descriptor); } // // Ok. We have a def. let's figure out what to do with it. // Set<DefDescriptor<?>> newDeps = Sets.newHashSet(); cd.def.appendDependencies(newDeps); // // FIXME: this code will go away with preloads. // This pulls in the context preloads. not pretty, but it works. // if (!cc.addedPreloads && cd.descriptor.getDefType().equals(DefType.APPLICATION)) { cc.addedPreloads = true; Set<String> preloads = cc.context.getPreloads(); for (String preload : preloads) { if (!preload.contains("_")) { DependencyDefImpl.Builder ddb = new DependencyDefImpl.Builder(); ddb.setResource(preload); ddb.setType("APPLICATION,COMPONENT,STYLE,EVENT"); ddb.build().appendDependencies(newDeps); } } } for (DefDescriptor<?> dep : newDeps) { if (!defs.containsKey(dep)) { CompilingDef<?> depcd = cc.getCompiling(dep); depcd.parents.add(cd.def); } } deps.addAll(newDeps); cc.dependencies.put(cd.descriptor, cd.def); return cd.def; } catch (DefinitionNotFoundException dnfe) { // // In the case that we have a DefinitionNotFoundException for our current descriptor, // cache the fact that we didn't find one. // if (dnfe.getDescriptor().equals(descriptor)) { cd.def = null; defs.put(descriptor, cd.def); if (cd.cacheable) { defsCache.put(descriptor, Optional.fromNullable(cd.def)); } } throw dnfe; } }
/** * Fill a compiling def for a descriptor. * * <p>This makes sure that we can get a registry for a given def, then tries to get the def from * the global cache, if that fails, it retrieves from the registry, and marks the def as locally * built. * * @param compiling the current compiling def (if there is one). * @throws QuickFixException if validateDefinition caused a quickfix. */ private <D extends Definition> boolean fillCompilingDef( CompilingDef<D> compiling, AuraContext context) throws QuickFixException { assert compiling.def == null; { // // First, check our local cached defs to see if we have a fully compiled version. // in this case, we don't care about caching, since we are done. // @SuppressWarnings("unchecked") D localDef = (D) defs.get(compiling.descriptor); if (localDef != null) { compiling.def = localDef; compiling.built = !localDef.isValid(); if (compiling.built) { localDef.validateDefinition(); } return true; } } // // If there is no local cache, we must first check to see if there is a registry, as we may not // have // a registry (depending on configuration). In the case that we don't find one, we are done // here. // DefRegistry<D> registry = getRegistryFor(compiling.descriptor); if (registry == null) { defs.put(compiling.descriptor, null); return false; } // // Now, check if we can cache the def later, as we won't have the registry to check at a later // time. // If we can cache, look it up in the cache. If we find it, we have a built definition. // if (isCacheable(registry)) { compiling.cacheable = true; @SuppressWarnings("unchecked") Optional<D> opt = (Optional<D>) defsCache.getIfPresent(compiling.descriptor); if (opt != null) { D cachedDef = opt.orNull(); if (cachedDef != null) { @SuppressWarnings("unchecked") DefDescriptor<D> canonical = (DefDescriptor<D>) cachedDef.getDescriptor(); compiling.def = cachedDef; compiling.descriptor = canonical; compiling.built = false; defs.put(canonical, cachedDef); return true; } else { return false; } } } // // The last case. This is our first compile or the def is uncacheable. // In this case, we make sure that the initial validation is called, and put // the def in the non-validated set. // compiling.def = registry.getDef(compiling.descriptor); if (compiling.def == null) { return false; } @SuppressWarnings("unchecked") DefDescriptor<D> canonical = (DefDescriptor<D>) compiling.def.getDescriptor(); compiling.descriptor = canonical; // cc.loggingService.incrementNum(LoggingService.DEF_COUNT); // FIXME: setting the current namespace on the context seems // extremely hackish context.setCurrentNamespace(canonical.getNamespace()); compiling.def.validateDefinition(); compiling.built = true; defs.put(canonical, compiling.def); return true; }