public void call(SourceUnit source) throws CompilationFailedException { List<ClassNode> classes = source.ast.getClasses(); for (ClassNode node : classes) { CompileUnit cu = node.getCompileUnit(); for (Iterator iter = cu.iterateClassNodeToCompile(); iter.hasNext(); ) { String name = (String) iter.next(); SourceUnit su = ast.getScriptSourceLocation(name); List<ClassNode> classesInSourceUnit = su.ast.getClasses(); StringBuilder message = new StringBuilder(); message .append("Compilation incomplete: expected to find the class ") .append(name) .append(" in ") .append(su.getName()); if (classesInSourceUnit.isEmpty()) { message.append(", but the file seems not to contain any classes"); } else { message.append(", but the file contains the classes: "); boolean first = true; for (ClassNode cn : classesInSourceUnit) { if (!first) { message.append(", "); } else { first = false; } message.append(cn.getName()); } } getErrorCollector() .addErrorAndContinue(new SimpleMessage(message.toString(), CompilationUnit.this)); iter.remove(); } } }
/** * A loop driver for applying operations to all primary ClassNodes in our AST. Automatically skips * units that have already been processed through the current phase. */ public void applyToPrimaryClassNodes(PrimaryClassNodeOperation body) throws CompilationFailedException { // GRECLIPSE: start /*old{ Iterator classNodes = getPrimaryClassNodes(body.needSortedInput()).iterator(); }*/ // newcode List primaryClassNodes = getPrimaryClassNodes(body.needSortedInput()); Iterator classNodes = primaryClassNodes.iterator(); // end while (classNodes.hasNext()) { SourceUnit context = null; try { ClassNode classNode = (ClassNode) classNodes.next(); context = classNode.getModule().getContext(); // GRECLIPSE get to the bottom of this - why are operations running multiple times that // should only run once? if (context == null || context.phase < phase || (context.phase == phase && !context.phaseComplete)) { int offset = 1; Iterator<InnerClassNode> iterator = classNode.getInnerClasses(); while (iterator.hasNext()) { iterator.next(); offset++; } body.call(context, new GeneratorContext(this.ast, offset), classNode); } } catch (CompilationFailedException e) { // fall through, getErrorReporter().failIfErrors() will trigger } catch (NullPointerException npe) { throw npe; } catch (GroovyBugError e) { changeBugText(e, context); throw e; } catch (Exception e) { // check the exception for a nested compilation exception ErrorCollector nestedCollector = null; for (Throwable next = e.getCause(); next != e && next != null; next = next.getCause()) { if (!(next instanceof MultipleCompilationErrorsException)) continue; MultipleCompilationErrorsException mcee = (MultipleCompilationErrorsException) next; nestedCollector = mcee.collector; break; } if (nestedCollector != null) { getErrorCollector().addCollectorContents(nestedCollector); } else { getErrorCollector().addError(new ExceptionMessage(e, configuration.getDebug(), this)); } } } getErrorCollector().failIfErrors(); }
private List<ClassNode> getSorted(int[] index, List<ClassNode> unsorted) { List<ClassNode> sorted = new ArrayList<ClassNode>(unsorted.size()); for (int i = 0; i < unsorted.size(); i++) { int min = -1; for (int j = 0; j < unsorted.size(); j++) { if (index[j] == -1) continue; if (min == -1) { min = j; } else if (index[j] < index[min]) { min = j; } } if (min == -1) break; sorted.add(unsorted.get(min)); index[min] = -1; } return sorted; }
/** * Dequeues any source units add through addSource and resets the compiler phase to * initialization. * * <p>Note: this does not mean a file is recompiled. If a SourceUnit has already passed a phase it * is skipped until a higher phase is reached. * * @return true if there was a queued source * @throws CompilationFailedException */ protected boolean dequeued() throws CompilationFailedException { boolean dequeue = !queuedSources.isEmpty(); while (!queuedSources.isEmpty()) { SourceUnit su = queuedSources.removeFirst(); String name = su.getName(); // GRECLIPSE: start if (iterating) { GroovyBugError gbe = new GroovyBugError( "Damaging 'names' whilst already iterating. Name getting added is '" + su.getName() + "'"); gbe.printStackTrace(); throw gbe; } // end names.add(name); sources.put(name, su); } if (dequeue) { gotoPhase(Phases.INITIALIZATION); } return dequeue; }
public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException { optimizer.visitClass( classNode, source); // GROOVY-4272: repositioned it here from staticImport if (!classNode.isSynthetic()) { GenericsVisitor genericsVisitor = new GenericsVisitor(source); genericsVisitor.visitClass(classNode); } // // Run the Verifier on the outer class // try { verifier.visitClass(classNode); } catch (GroovyRuntimeException rpe) { ASTNode node = rpe.getNode(); getErrorCollector() .addError( new SyntaxException( rpe.getMessage(), node.getLineNumber(), node.getColumnNumber(), node.getLastLineNumber(), node.getLastColumnNumber()), source); } LabelVerifier lv = new LabelVerifier(source); lv.visitClass(classNode); ClassCompletionVerifier completionVerifier = new ClassCompletionVerifier(source); completionVerifier.visitClass(classNode); ExtendedVerifier xverifier = new ExtendedVerifier(source); xverifier.visitClass(classNode); // because the class may be generated even if a error was found // and that class may have an invalid format we fail here if needed getErrorCollector().failIfErrors(); // // Prep the generator machinery // ClassVisitor visitor = createClassVisitor(); String sourceName = (source == null ? classNode.getModule().getDescription() : source.getName()); // only show the file name and its extension like javac does in its stacktraces rather // than the full path // also takes care of both \ and / depending on the host compiling environment if (sourceName != null) sourceName = sourceName.substring( Math.max(sourceName.lastIndexOf('\\'), sourceName.lastIndexOf('/')) + 1); AsmClassGenerator generator = new AsmClassGenerator(source, context, visitor, sourceName); // // Run the generation and create the class (if required) // // GRECLIPSE: if there are errors, don't generate code. // code gen can fail unexpectedly if there was an earlier error. if (!source.getErrorCollector().hasErrors()) { // end generator.visitClass(classNode); byte[] bytes = ((ClassWriter) visitor).toByteArray(); /// GRECLIPSE: start: added classNode, sourceUnit /*old{ generatedClasses.add(new GroovyClass(classNode.getName(), bytes)); }*/ // newcode generatedClasses.add(new GroovyClass(classNode.getName(), bytes, classNode, source)); // end // // Handle any callback that's been set // if (CompilationUnit.this.classgenCallback != null) { classgenCallback.call(visitor, classNode); } // // Recurse for inner classes // LinkedList innerClasses = generator.getInnerClasses(); while (!innerClasses.isEmpty()) { classgen.call(source, context, (ClassNode) innerClasses.removeFirst()); } // GRECLIPSE: if there are errors, don't generate code } // end }
private List getPrimaryClassNodes(boolean sort) { if (sort == true) { List<ModuleNode> sortedModules = this.ast.getSortedModules(); if (sortedModules != null) { return sortedModules; } } // FIXASC (groovychange) rewritten /*old{ List unsorted = new ArrayList(); Iterator modules = this.ast.getModules().iterator(); while (modules.hasNext()) { ModuleNode module = (ModuleNode) modules.next(); Iterator classNodes = module.getClasses().iterator(); while (classNodes.hasNext()) { ClassNode classNode = (ClassNode) classNodes.next(); unsorted.add(classNode); } } */ // new List<ClassNode> unsorted = new ArrayList<ClassNode>(); for (ModuleNode module : this.ast.getModules()) { unsorted.addAll(module.getClasses()); } // FIXASC (groovychange) end if (!sort) return unsorted; // GRECLIPSE: start: rewritten sort algorithm /*old{ int[] indexClass = new int[unsorted.size()]; int[] indexInterface = new int[unsorted.size()]; { int i = 0; for (Iterator<ClassNode> iter = unsorted.iterator(); iter.hasNext(); i++) { ClassNode element = iter.next(); if (element.isInterface()) { indexInterface[i] = getSuperInterfaceCount(element); indexClass[i] = -1; } else { indexClass[i] = getSuperClassCount(element); indexInterface[i] = -1; } } } List<ClassNode> sorted = getSorted(indexInterface, unsorted); sorted.addAll(getSorted(indexClass, unsorted)); */ // newcode: // Sort them by how many types are in their hierarchy, but all interfaces first. // Algorithm: // Create a list of integers. Each integer captures the index into the unsorted // list (bottom 16bits) and the count of how many types are in that types // hierarchy (top 16bits). For classes the count is augmented by 2000 so that // when sorting the classes will come out after the interfaces. // This list of integers is sorted. We then just go through it and for the // lower 16bits of each entry (0xffff) that is the index of the next value to // pull from the unsorted list and put into the sorted list. // Will break down if more than 2000 interfaces in the type hierarchy for an // individual type, or a project contains > 65535 files... but if you've got // that kind of setup, you have other problems... List<Integer> countIndexPairs = new ArrayList<Integer>(); { int i = 0; for (Iterator iter = unsorted.iterator(); iter.hasNext(); i++) { ClassNode node = (ClassNode) iter.next(); if (node.isInterface()) { countIndexPairs.add((getSuperInterfaceCount(node) << 16) + i); } else { countIndexPairs.add(((getSuperClassCount(node) + 2000) << 16) + i); } } } Collections.sort(countIndexPairs); List sorted = new ArrayList(); for (int i : countIndexPairs) { sorted.add(unsorted.get(i & 0xffff)); } this.ast.setSortedModules(sorted); // end return sorted; }