@Override public Void visitXmlTagNode(XmlTagNode node) { if (isScriptNode(node)) { Source htmlSource = htmlElement.getSource(); String scriptSourcePath = getScriptSourcePath(node); if (node.getAttributeEnd().getType() == TokenType.GT && scriptSourcePath == null) { EmbeddedHtmlScriptElementImpl script = new EmbeddedHtmlScriptElementImpl(node); String contents = node.getContent(); // TODO (danrubel): Move scanning embedded scripts into AnalysisContextImpl // so that clients such as builder can scan, parse, and get errors without resolving AnalysisErrorListener errorListener = new AnalysisErrorListener() { @Override public void onError(AnalysisError error) { // TODO (danrubel): make errors accessible once this is moved into // AnalysisContextImpl } }; StringScanner scanner = new StringScanner(null, contents, errorListener); com.google.dart.engine.scanner.Token firstToken = scanner.tokenize(); int[] lineStarts = scanner.getLineStarts(); // TODO (danrubel): Move parsing embedded scripts into AnalysisContextImpl // so that clients such as builder can scan, parse, and get errors without resolving Parser parser = new Parser(null, errorListener); CompilationUnit unit = parser.parseCompilationUnit(firstToken); try { CompilationUnitBuilder builder = new CompilationUnitBuilder(); CompilationUnitElementImpl elem = builder.buildCompilationUnit(htmlSource, unit); LibraryElementImpl library = new LibraryElementImpl(context, null); library.setDefiningCompilationUnit(elem); script.setScriptLibrary(library); } catch (AnalysisException e) { // TODO (danrubel): Handle or forward the exception e.printStackTrace(); } scripts.add(script); } else { ExternalHtmlScriptElementImpl script = new ExternalHtmlScriptElementImpl(node); if (scriptSourcePath != null) { script.setScriptSource( context.getSourceFactory().resolveUri(htmlSource, scriptSourcePath)); } scripts.add(script); } } else { node.visitChildren(this); } return null; }
@Override public void process( CompilationUnitBuilder builder, CompilationUnitDeclaration cud, List<CompiledClass> compiledClasses) { builder .setClasses(compiledClasses) .setTypes(Collections.<JDeclaredType>emptyList()) .setDependencies(new Dependencies()) .setJsniMethods(Collections.<JsniMethod>emptyList()) .setMethodArgs(new MethodArgNamesLookup()) .setProblems(cud.compilationResult().getProblems()); results.add(builder.build()); }
public boolean doCompile(Collection<CompilationUnitBuilder> builders) { List<ICompilationUnit> icus = new ArrayList<ICompilationUnit>(); for (CompilationUnitBuilder builder : builders) { addPackages(Shared.getPackageName(builder.getTypeName()).replace('.', '/')); icus.add(new Adapter(builder)); } if (icus.isEmpty()) { return false; } compilerImpl = new CompilerImpl(); compilerImpl.compile(icus.toArray(new ICompilationUnit[icus.size()])); compilerImpl = null; return true; }
/** * Compile new generated units into an existing state. * * <p>TODO: maybe use a finer brush than to synchronize the whole thing. */ synchronized Collection<CompilationUnit> doBuildGeneratedTypes( TreeLogger logger, CompilerContext compilerContext, Collection<GeneratedUnit> generatedUnits, CompileMoreLater compileMoreLater) throws UnableToCompleteException { UnitCache unitCache = compilerContext.getUnitCache(); // Units we definitely want to build. List<CompilationUnitBuilder> builders = new ArrayList<CompilationUnitBuilder>(); // Units we don't want to rebuild unless we have to. Map<CompilationUnitBuilder, CompilationUnit> cachedUnits = new IdentityHashMap<CompilationUnitBuilder, CompilationUnit>(); // For each incoming generated Java source file... for (GeneratedUnit generatedUnit : generatedUnits) { // Create a builder for all incoming units. CompilationUnitBuilder builder = CompilationUnitBuilder.create(generatedUnit); // Look for units previously compiled CompilationUnit cachedUnit = unitCache.find(builder.getContentId()); if (cachedUnit != null) { // Recompile generated units with errors so source can be dumped. if (!cachedUnit.isError()) { cachedUnits.put(builder, cachedUnit); compileMoreLater.addValidUnit(cachedUnit); continue; } } builders.add(builder); } return compileMoreLater.compile( logger, compilerContext, builders, cachedUnits, CompilerEventType.JDT_COMPILER_CSB_GENERATED); }
@Override public NameEnvironmentAnswer findType(char[][] compoundTypeName) { char[] binaryNameChars = CharOperation.concatWith(compoundTypeName, '/'); String binaryName = String.valueOf(binaryNameChars); CompiledClass compiledClass = binaryTypes.get(binaryName); try { if (compiledClass != null) { return compiledClass.getNameEnvironmentAnswer(); } } catch (ClassFormatException ex) { // fall back to binary class } if (isPackage(binaryName)) { return null; } if (additionalTypeProviderDelegate != null) { GeneratedUnit unit = additionalTypeProviderDelegate.doFindAdditionalType(binaryName); if (unit != null) { CompilationUnitBuilder b = CompilationUnitBuilder.create(unit); Adapter a = new Adapter(b); return new NameEnvironmentAnswer(a, null); } } try { URL resource = getClassLoader().getResource(binaryName + ".class"); if (resource != null) { InputStream openStream = resource.openStream(); try { ClassFileReader cfr = ClassFileReader.read(openStream, resource.toExternalForm(), true); return new NameEnvironmentAnswer(cfr, null); } finally { Utility.close(openStream); } } } catch (ClassFormatException e) { } catch (IOException e) { } return null; }
@Override public String toString() { return builder.toString(); }
@Override public char[][] getPackageName() { String packageName = Shared.getPackageName(builder.getTypeName()); return CharOperation.splitOn('.', packageName.toCharArray()); }
@Override public char[] getMainTypeName() { return Shared.getShortName(builder.getTypeName()).toCharArray(); }
@Override public char[] getFileName() { return builder.getLocation().toCharArray(); }
@Override public char[] getContents() { return builder.getSource().toCharArray(); }
/** * A callback after the JDT compiler has compiled a .java file and created a matching * CompilationUnitDeclaration. We take this opportunity to create a matching CompilationUnit. */ @Override public void process( CompilationUnitBuilder builder, CompilationUnitDeclaration cud, List<CompiledClass> compiledClasses) { Event event = SpeedTracerLogger.start(DevModeEventType.CSB_PROCESS); try { Map<MethodDeclaration, JsniMethod> jsniMethods = JsniCollector.collectJsniMethods( cud, builder.getSourceMapPath(), builder.getSource(), JsRootScope.INSTANCE, DummyCorrelationFactory.INSTANCE); JSORestrictionsChecker.check(jsoState, cud); // JSNI check + collect dependencies. final Set<String> jsniDeps = new HashSet<String>(); Map<String, Binding> jsniRefs = new HashMap<String, Binding>(); JsniChecker.check( cud, jsoState, jsniMethods, jsniRefs, new JsniChecker.TypeResolver() { @Override public ReferenceBinding resolveType(String sourceOrBinaryName) { ReferenceBinding resolveType = compiler.resolveType(sourceOrBinaryName); if (resolveType != null) { jsniDeps.add(String.valueOf(resolveType.qualifiedSourceName())); } return resolveType; } }); Map<TypeDeclaration, Binding[]> artificialRescues = new HashMap<TypeDeclaration, Binding[]>(); ArtificialRescueChecker.check(cud, builder.isGenerated(), artificialRescues); if (compilerContext.shouldCompileMonolithic()) { // GWT drives JDT in a way that allows missing references in the source to be resolved // to precompiled bytecode on disk (see INameEnvironment). This is done so that // annotations can be supplied in bytecode form only. But since no AST is available for // these types it creates the danger that some functional class (not just an annotation) // gets filled in but is missing AST. This would cause later compiler stages to fail. // // Library compilation needs to ignore this check since it is expected behavior for the // source being compiled in a library to make references to other types which are only // available as bytecode coming out of dependency libraries. // // But if the referenced bytecode did not come from a dependency library but instead was // free floating in the classpath, then there is no guarrantee that AST for it was ever // seen and translated to JS anywhere in the dependency tree. This would be a mistake. // // TODO(stalcup): add a more specific check for library compiles such that binary types // can be referenced but only if they are an Annotation or if the binary type comes from // a dependency library. BinaryTypeReferenceRestrictionsChecker.check(cud); } MethodArgNamesLookup methodArgs = MethodParamCollector.collect(cud, builder.getSourceMapPath()); Interner<String> interner = StringInterner.get(); String packageName = interner.intern(Shared.getPackageName(builder.getTypeName())); List<String> unresolvedQualified = new ArrayList<String>(); List<String> unresolvedSimple = new ArrayList<String>(); for (char[] simpleRef : cud.compilationResult().simpleNameReferences) { unresolvedSimple.add(interner.intern(String.valueOf(simpleRef))); } for (char[][] qualifiedRef : cud.compilationResult().qualifiedReferences) { unresolvedQualified.add(interner.intern(CharOperation.toString(qualifiedRef))); } for (String jsniDep : jsniDeps) { unresolvedQualified.add(interner.intern(jsniDep)); } ArrayList<String> apiRefs = compiler.collectApiRefs(cud); for (int i = 0; i < apiRefs.size(); ++i) { apiRefs.set(i, interner.intern(apiRefs.get(i))); } Dependencies dependencies = new Dependencies(packageName, unresolvedQualified, unresolvedSimple, apiRefs); List<JDeclaredType> types = Collections.emptyList(); if (!cud.compilationResult().hasErrors()) { // Make a GWT AST. types = astBuilder.process( cud, builder.getSourceMapPath(), artificialRescues, jsniMethods, jsniRefs); } for (CompiledClass cc : compiledClasses) { allValidClasses.put(cc.getSourceName(), cc); } builder .setClasses(compiledClasses) .setTypes(types) .setDependencies(dependencies) .setJsniMethods(jsniMethods.values()) .setMethodArgs(methodArgs) .setProblems(cud.compilationResult().getProblems()); buildQueue.add(builder); } finally { event.end(); } }
/** * Build a new compilation state from a source oracle. Allow the caller to specify a compiler * delegate that will handle undefined names. * * <p>TODO: maybe use a finer brush than to synchronize the whole thing. */ public synchronized CompilationState doBuildFrom( TreeLogger logger, CompilerContext compilerContext, Set<Resource> resources, AdditionalTypeProviderDelegate compilerDelegate) throws UnableToCompleteException { UnitCache unitCache = compilerContext.getUnitCache(); // Units we definitely want to build. List<CompilationUnitBuilder> builders = new ArrayList<CompilationUnitBuilder>(); // Units we don't want to rebuild unless we have to. Map<CompilationUnitBuilder, CompilationUnit> cachedUnits = new IdentityHashMap<CompilationUnitBuilder, CompilationUnit>(); CompileMoreLater compileMoreLater = new CompileMoreLater(compilerContext, compilerDelegate); // For each incoming Java source file... for (Resource resource : resources) { // Create a builder for all incoming units. CompilationUnitBuilder builder = CompilationUnitBuilder.create(resource); CompilationUnit cachedUnit = unitCache.find(resource.getPathPrefix() + resource.getPath()); // Try to rescue cached units from previous sessions where a jar has been // recompiled. if (cachedUnit != null && cachedUnit.getLastModified() != resource.getLastModified()) { unitCache.remove(cachedUnit); if (cachedUnit instanceof CachedCompilationUnit && cachedUnit.getContentId().equals(builder.getContentId())) { CachedCompilationUnit updatedUnit = new CachedCompilationUnit( (CachedCompilationUnit) cachedUnit, resource.getLastModified(), resource.getLocation()); unitCache.add(updatedUnit); } else { cachedUnit = null; } } if (cachedUnit != null) { cachedUnits.put(builder, cachedUnit); compileMoreLater.addValidUnit(cachedUnit); continue; } builders.add(builder); } if (logger.isLoggable(TreeLogger.TRACE)) { logger.log( TreeLogger.TRACE, "Found " + cachedUnits.size() + " cached/archived units. Used " + cachedUnits.size() + " / " + resources.size() + " units from cache."); } Collection<CompilationUnit> resultUnits = compileMoreLater.compile( logger, compilerContext, builders, cachedUnits, CompilerEventType.JDT_COMPILER_CSB_FROM_ORACLE); boolean compileMonolithic = compilerContext.shouldCompileMonolithic(); TypeOracle typeOracle = null; CompilationUnitTypeOracleUpdater typeOracleUpdater = null; if (compileMonolithic) { typeOracle = new TypeOracle(); typeOracleUpdater = new CompilationUnitTypeOracleUpdater(typeOracle); } else { typeOracle = new LibraryTypeOracle(compilerContext); typeOracleUpdater = ((LibraryTypeOracle) typeOracle).getTypeOracleUpdater(); } return new CompilationState( logger, compilerContext, typeOracle, typeOracleUpdater, resultUnits, compileMoreLater); }
/** * Compiles the source code in each supplied CompilationUnitBuilder into a CompilationUnit and * reports errors. * * <p>A compilation unit is considered invalid if any of its dependencies (recursively) isn't * being compiled and isn't in allValidClasses, or if it has a signature that doesn't match a * dependency. Valid compilation units will be added to cachedUnits and the unit cache, and * their types will be added to allValidClasses. Invalid compilation units will be removed. * * <p>I/O: serializes the AST of each Java type to DiskCache. (This happens even if the * compilation unit is later dropped.) If we're using the persistent unit cache, each valid unit * will also be serialized to the gwt-unitcache file. (As a result, each AST will be copied * there from the DiskCache.) A new persistent unit cache file will be created each time * compile() is called (if there's at least one valid unit) and the entire cache will be * rewritten to disk every {@link PersistentUnitCache#CACHE_FILE_THRESHOLD} files. * * <p>This function won't report errors in invalid source files unless suppressErrors is false. * Instead, a summary giving the number of invalid files will be logged. * * <p>If the JDT compiler aborts, logs an error and throws UnableToCompleteException. (This * doesn't happen for normal compile errors.) */ Collection<CompilationUnit> compile( TreeLogger logger, CompilerContext compilerContext, Collection<CompilationUnitBuilder> builders, Map<CompilationUnitBuilder, CompilationUnit> cachedUnits, EventType eventType) throws UnableToCompleteException { UnitCache unitCache = compilerContext.getUnitCache(); // Initialize the set of valid classes to the initially cached units. for (CompilationUnit unit : cachedUnits.values()) { for (CompiledClass cc : unit.getCompiledClasses()) { // Map by source name. String sourceName = cc.getSourceName(); allValidClasses.put(sourceName, cc); } } ArrayList<CompilationUnit> resultUnits = new ArrayList<CompilationUnit>(); do { final TreeLogger branch = logger.branch(TreeLogger.TRACE, "Compiling..."); // Compile anything that needs to be compiled. buildQueue = new LinkedBlockingQueue<CompilationUnitBuilder>(); final ArrayList<CompilationUnit> newlyBuiltUnits = new ArrayList<CompilationUnit>(); final CompilationUnitBuilder sentinel = CompilationUnitBuilder.create((GeneratedUnit) null); final Throwable[] workerException = new Throwable[1]; final ProgressLogger progressLogger = new ProgressLogger(branch, TreeLogger.TRACE, builders.size(), 10); Thread buildThread = new Thread() { @Override public void run() { int processedCompilationUnitBuilders = 0; try { do { CompilationUnitBuilder builder = buildQueue.take(); if (!progressLogger.isTimerStarted()) { // Set start time here, after first job has arrived, since it can take a // little // while for the first job to arrive, and this helps with the accuracy of the // estimated times. progressLogger.startTimer(); } if (builder == sentinel) { return; } // Expensive, must serialize GWT AST types to bytes. CompilationUnit unit = builder.build(); newlyBuiltUnits.add(unit); processedCompilationUnitBuilders++; progressLogger.updateProgress(processedCompilationUnitBuilders); } while (true); } catch (Throwable e) { workerException[0] = e; } } }; buildThread.setName("CompilationUnitBuilder"); buildThread.start(); Event jdtCompilerEvent = SpeedTracerLogger.start(eventType); long compilationStartNanos = System.nanoTime(); try { compiler.doCompile(branch, builders); } finally { jdtCompilerEvent.end(); } buildQueue.add(sentinel); try { buildThread.join(); long compilationNanos = System.nanoTime() - compilationStartNanos; // Convert nanos to seconds. double compilationSeconds = compilationNanos / (double) TimeUnit.SECONDS.toNanos(1); branch.log( TreeLogger.TRACE, String.format("Compilation completed in %.02f seconds", compilationSeconds)); if (workerException[0] != null) { throw workerException[0]; } } catch (RuntimeException e) { throw e; } catch (Throwable e) { throw new RuntimeException("Exception processing units", e); } finally { buildQueue = null; } resultUnits.addAll(newlyBuiltUnits); builders.clear(); // Resolve all newly built unit deps against the global classes. for (CompilationUnit unit : newlyBuiltUnits) { unit.getDependencies().resolve(allValidClasses); } /* * Invalidate any cached units with invalid refs. */ Collection<CompilationUnit> invalidatedUnits = new ArrayList<CompilationUnit>(); for (Iterator<Entry<CompilationUnitBuilder, CompilationUnit>> it = cachedUnits.entrySet().iterator(); it.hasNext(); ) { Entry<CompilationUnitBuilder, CompilationUnit> entry = it.next(); CompilationUnit unit = entry.getValue(); boolean isValid = unit.getDependencies().validate(logger, allValidClasses); if (isValid && unit.isError()) { // See if the unit has classes that can't provide a // NameEnvironmentAnswer for (CompiledClass cc : unit.getCompiledClasses()) { try { cc.getNameEnvironmentAnswer(); } catch (ClassFormatException ex) { isValid = false; break; } } } if (!isValid) { if (logger.isLoggable(TreeLogger.TRACE)) { logger.log(TreeLogger.TRACE, "Invalid Unit: " + unit.getTypeName()); } invalidatedUnits.add(unit); builders.add(entry.getKey()); it.remove(); } } if (invalidatedUnits.size() > 0) { if (logger.isLoggable(TreeLogger.TRACE)) { logger.log(TreeLogger.TRACE, "Invalid units found: " + invalidatedUnits.size()); } } // Any units we invalidated must now be removed from the valid classes. for (CompilationUnit unit : invalidatedUnits) { for (CompiledClass cc : unit.getCompiledClasses()) { allValidClasses.remove(cc.getSourceName()); } } } while (builders.size() > 0); for (CompilationUnit unit : resultUnits) { unitCache.add(unit); } // Any remaining cached units are valid now. resultUnits.addAll(cachedUnits.values()); // Done with a pass of the build - tell the cache its OK to cleanup // stale cache files. unitCache.cleanup(logger); // Sort, then report all errors (re-report for cached units). Collections.sort(resultUnits, CompilationUnit.COMPARATOR); logger = logger.branch(TreeLogger.DEBUG, "Validating units:"); int errorCount = 0; for (CompilationUnit unit : resultUnits) { if (CompilationProblemReporter.reportErrors(logger, unit, suppressErrors)) { errorCount++; } } if (suppressErrors && errorCount > 0 && !logger.isLoggable(TreeLogger.TRACE) && logger.isLoggable(TreeLogger.INFO)) { logger.log( TreeLogger.INFO, "Ignored " + errorCount + " unit" + (errorCount > 1 ? "s" : "") + " with compilation errors in first pass.\n" + "Compile with -strict or with -logLevel set to TRACE or DEBUG to see all errors."); } return resultUnits; }