Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 09876200D6F for ; Sun, 17 Dec 2017 15:01:19 +0100 (CET) Received: by cust-asf.ponee.io (Postfix) id 07B5B160C55; Sun, 17 Dec 2017 14:01:19 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 2CD83160C38 for ; Sun, 17 Dec 2017 15:01:17 +0100 (CET) Received: (qmail 99900 invoked by uid 500); 17 Dec 2017 14:01:16 -0000 Mailing-List: contact commits-help@groovy.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@groovy.apache.org Delivered-To: mailing list commits@groovy.apache.org Received: (qmail 99772 invoked by uid 99); 17 Dec 2017 14:01:16 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 17 Dec 2017 14:01:16 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 0F0CDF17D9; Sun, 17 Dec 2017 14:01:12 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: cchampeau@apache.org To: commits@groovy.apache.org Date: Sun, 17 Dec 2017 14:01:25 -0000 Message-Id: In-Reply-To: <44b1520b4d1b4cfb8f0b44e087731315@git.apache.org> References: <44b1520b4d1b4cfb8f0b44e087731315@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [14/62] [abbrv] [partial] groovy git commit: Move Java source set into `src/main/java` archived-at: Sun, 17 Dec 2017 14:01:19 -0000 http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/control/ErrorCollector.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/control/ErrorCollector.java b/src/main/java/org/codehaus/groovy/control/ErrorCollector.java new file mode 100644 index 0000000..b388caa --- /dev/null +++ b/src/main/java/org/codehaus/groovy/control/ErrorCollector.java @@ -0,0 +1,348 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.codehaus.groovy.control; + +import org.codehaus.groovy.control.messages.ExceptionMessage; +import org.codehaus.groovy.control.messages.LocatedMessage; +import org.codehaus.groovy.control.messages.Message; +import org.codehaus.groovy.control.messages.SyntaxErrorMessage; +import org.codehaus.groovy.control.messages.WarningMessage; +import org.codehaus.groovy.syntax.CSTNode; +import org.codehaus.groovy.syntax.SyntaxException; + +import java.io.PrintWriter; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +/** + * A base class for collecting messages and errors during processing. + * Each CompilationUnit should have an ErrorCollector, and the SourceUnits + * should share their ErrorCollector with the CompilationUnit. + * + * @author Chris Poirier + * @author Jochen Theodorou + */ +public class ErrorCollector { + + /** + * WarningMessages collected during processing + */ + protected LinkedList warnings; + /** + * ErrorMessages collected during processing + */ + protected LinkedList errors; + /** + * Configuration and other settings that control processing + */ + protected CompilerConfiguration configuration; + + /** + * Initialize the ErrorReporter. + */ + public ErrorCollector(CompilerConfiguration configuration) { + this.warnings = null; + this.errors = null; + + this.configuration = configuration; + } + + public void addCollectorContents(ErrorCollector er) { + if (er.errors!=null) { + if (errors==null) { + errors = er.errors; + } else { + errors.addAll(er.errors); + } + } + if (er.warnings!=null) { + if (warnings==null) { + warnings = er.warnings; + } else { + warnings.addAll(er.warnings); + } + } + } + + public void addErrorAndContinue(SyntaxException error, SourceUnit source) throws CompilationFailedException { + addErrorAndContinue(Message.create(error, source)); + } + + /** + * Adds an error to the message set, but does not cause a failure. The message is not required to have a source + * line and column specified, but it is best practice to try and include that information. + */ + public void addErrorAndContinue(Message message) { + if (this.errors == null) { + this.errors = new LinkedList(); + } + + this.errors.add(message); + } + + /** + * Adds a non-fatal error to the message set, which may cause a failure if the error threshold is exceeded. + * The message is not required to have a source line and column specified, but it is best practice to try + * and include that information. + */ + public void addError(Message message) throws CompilationFailedException { + addErrorAndContinue(message); + + if (errors!=null && this.errors.size() >= configuration.getTolerance()) { + failIfErrors(); + } + } + + /** + * Adds an optionally-fatal error to the message set. + * The message is not required to have a source line and column specified, but it is best practice to try + * and include that information. + * @param fatal + * if true then then processing will stop + */ + public void addError(Message message, boolean fatal) throws CompilationFailedException { + if (fatal) { + addFatalError(message); + } + else { + addError(message); + } + } + + + /** + * Convenience wrapper for addError(). + */ + public void addError(SyntaxException error, SourceUnit source) throws CompilationFailedException { + addError(Message.create(error, source), error.isFatal()); + } + + + /** + * Convenience wrapper for addError(). + */ + public void addError(String text, CSTNode context, SourceUnit source) throws CompilationFailedException { + addError(new LocatedMessage(text, context, source)); + } + + + /** + * Adds a fatal exception to the message set and throws + * the unit as a PhaseFailedException. + */ + public void addFatalError(Message message) throws CompilationFailedException { + addError(message); + failIfErrors(); + } + + + public void addException(Exception cause, SourceUnit source) throws CompilationFailedException { + addError(new ExceptionMessage(cause,configuration.getDebug(),source)); + failIfErrors(); + } + + /** + * Returns true if there are any errors pending. + */ + public boolean hasErrors() { + return this.errors != null; + } + + /** + * @return the compiler configuration used to create this error collector + */ + public CompilerConfiguration getConfiguration() { + return configuration; + } + + /** + * Returns true if there are any warnings pending. + */ + public boolean hasWarnings() { + return this.warnings != null; + } + + /** + * Returns the list of warnings, or null if there are none. + */ + public List getWarnings() { + return this.warnings; + } + + /** + * Returns the list of errors, or null if there are none. + */ + public List getErrors() { + return this.errors; + } + + /** + * Returns the number of warnings. + */ + public int getWarningCount() { + return ((this.warnings == null) ? 0 : this.warnings.size()); + } + + /** + * Returns the number of errors. + */ + public int getErrorCount() { + return ((this.errors == null) ? 0 : this.errors.size()); + } + + /** + * Returns the specified warning message, or null. + */ + public WarningMessage getWarning(int index) { + if (index < getWarningCount()) { + return (WarningMessage) this.warnings.get(index); + } + return null; + } + + /** + * Returns the specified error message, or null. + */ + public Message getError(int index) { + if (index < getErrorCount()) { + return (Message) this.errors.get(index); + } + return null; + } + + /** + * Returns the last error reported + */ + public Message getLastError() { + return (Message) this.errors.getLast(); + } + + /** + * Convenience routine to return the specified error's + * underlying SyntaxException, or null if it isn't one. + */ + public SyntaxException getSyntaxError(int index) { + SyntaxException exception = null; + + Message message = getError(index); + if (message != null && message instanceof SyntaxErrorMessage) { + exception = ((SyntaxErrorMessage) message).getCause(); + } + return exception; + } + + /** + * Convenience routine to return the specified error's + * underlying Exception, or null if it isn't one. + */ + public Exception getException(int index) { + Exception exception = null; + + Message message = getError(index); + if (message != null) { + if (message instanceof ExceptionMessage) { + exception = ((ExceptionMessage) message).getCause(); + } + else if (message instanceof SyntaxErrorMessage) { + exception = ((SyntaxErrorMessage) message).getCause(); + } + } + return exception; + } + + /** + * Adds a WarningMessage to the message set. + */ + public void addWarning(WarningMessage message) { + if (message.isRelevant(configuration.getWarningLevel())) { + if (this.warnings == null) { + this.warnings = new LinkedList(); + } + + this.warnings.add(message); + } + } + + + /** + * Convenience wrapper for addWarning() that won't create an object + * unless it is relevant. + */ + public void addWarning(int importance, String text, CSTNode context, SourceUnit source) { + if (WarningMessage.isRelevant(importance, configuration.getWarningLevel())) { + addWarning(new WarningMessage(importance, text, context, source)); + } + } + + + /** + * Convenience wrapper for addWarning() that won't create an object + * unless it is relevant. + */ + public void addWarning(int importance, String text, Object data, CSTNode context, SourceUnit source) { + if (WarningMessage.isRelevant(importance, configuration.getWarningLevel())) { + addWarning(new WarningMessage(importance, text, data, context, source)); + } + } + + + /** + * Causes the current phase to fail by throwing a + * CompilationFailedException. + */ + protected void failIfErrors() throws CompilationFailedException { + if (hasErrors()) { + throw new MultipleCompilationErrorsException(this); + } + } + + //--------------------------------------------------------------------------- + // OUTPUT + + + private void write(PrintWriter writer, Janitor janitor, List messages, String txt) { + if (messages==null || messages.isEmpty()) return; + Iterator iterator = messages.iterator(); + while (iterator.hasNext()) { + Message message = (Message) iterator.next(); + message.write(writer, janitor); + + if (configuration.getDebug() && (message instanceof SyntaxErrorMessage)){ + SyntaxErrorMessage sem = (SyntaxErrorMessage) message; + sem.getCause().printStackTrace(writer); + } + writer.println(); + } + + writer.print(messages.size()); + writer.print(" "+txt); + if (messages.size()>1) writer.print("s"); + writer.println(); + } + + /** + * Writes error messages to the specified PrintWriter. + */ + public void write(PrintWriter writer, Janitor janitor) { + write(writer,janitor,warnings,"warning"); + write(writer,janitor,errors,"error"); + } + +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/control/GenericsVisitor.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/control/GenericsVisitor.java b/src/main/java/org/codehaus/groovy/control/GenericsVisitor.java new file mode 100644 index 0000000..36ef5ea --- /dev/null +++ b/src/main/java/org/codehaus/groovy/control/GenericsVisitor.java @@ -0,0 +1,188 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.codehaus.groovy.control; + +import org.codehaus.groovy.ast.ClassCodeVisitorSupport; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.FieldNode; +import org.codehaus.groovy.ast.GenericsType; +import org.codehaus.groovy.ast.InnerClassNode; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.Parameter; +import org.codehaus.groovy.ast.expr.ConstructorCallExpression; + +/** + * class used to verify correct usage of generics in + * class header (class and superclass declaration) + * + * @author Jochen Theodorou + */ +public class GenericsVisitor extends ClassCodeVisitorSupport { + private final SourceUnit source; + + public GenericsVisitor(SourceUnit source) { + this.source = source; + } + + protected SourceUnit getSourceUnit() { + return source; + } + + @Override + public void visitClass(ClassNode node) { + boolean error = checkWildcard(node); + if (error) return; + boolean isAnon = node instanceof InnerClassNode && ((InnerClassNode)node).isAnonymous(); + checkGenericsUsage(node.getUnresolvedSuperClass(false), node.getSuperClass(), isAnon ? true : null); + ClassNode[] interfaces = node.getInterfaces(); + for (ClassNode anInterface : interfaces) { + checkGenericsUsage(anInterface, anInterface.redirect()); + } + node.visitContents(this); + } + + @Override + public void visitField(FieldNode node) { + ClassNode type = node.getType(); + checkGenericsUsage(type, type.redirect()); + super.visitField(node); + } + + @Override + public void visitConstructorCallExpression(ConstructorCallExpression call) { + ClassNode type = call.getType(); + boolean isAnon = type instanceof InnerClassNode && ((InnerClassNode)type).isAnonymous(); + checkGenericsUsage(type, type.redirect(), isAnon); + } + + @Override + public void visitMethod(MethodNode node) { + Parameter[] parameters = node.getParameters(); + for (Parameter param : parameters) { + ClassNode paramType = param.getType(); + checkGenericsUsage(paramType, paramType.redirect()); + } + ClassNode returnType = node.getReturnType(); + checkGenericsUsage(returnType, returnType.redirect()); + super.visitMethod(node); + } + + private boolean checkWildcard(ClassNode cn) { + ClassNode sn = cn.getUnresolvedSuperClass(false); + if (sn == null) return false; + GenericsType[] generics = sn.getGenericsTypes(); + if (generics == null) return false; + boolean error = false; + for (GenericsType generic : generics) { + if (generic.isWildcard()) { + addError("A supertype may not specify a wildcard type", sn); + error = true; + } + } + return error; + } + + private void checkGenericsUsage(ClassNode n, ClassNode cn) { + checkGenericsUsage(n, cn, null); + } + + private void checkGenericsUsage(ClassNode n, ClassNode cn, Boolean isAnonInnerClass) { + if (n.isGenericsPlaceHolder()) return; + GenericsType[] nTypes = n.getGenericsTypes(); + GenericsType[] cnTypes = cn.getGenericsTypes(); + // raw type usage is always allowed + if (nTypes == null) return; + // you can't parameterize a non-generified type + if (cnTypes == null) { + String message = "The class " + getPrintName(n) + " (supplied with " + plural("type parameter", nTypes.length) + + ") refers to the class " + getPrintName(cn) + " which takes no parameters"; + if (nTypes.length == 0) { + message += " (invalid Diamond <> usage?)"; + } + addError(message, n); + return; + } + // parameterize a type by using all of the parameters only + if (nTypes.length != cnTypes.length) { + if (Boolean.FALSE.equals(isAnonInnerClass) && nTypes.length == 0) { + return; // allow Diamond for non-AIC cases from CCE + } + String message; + if (Boolean.TRUE.equals(isAnonInnerClass) && nTypes.length == 0) { + message = "Cannot use diamond <> with anonymous inner classes"; + } else { + message = "The class " + getPrintName(n) + " (supplied with " + plural("type parameter", nTypes.length) + + ") refers to the class " + getPrintName(cn) + + " which takes " + plural("parameter", cnTypes.length); + if (nTypes.length == 0) { + message += " (invalid Diamond <> usage?)"; + } + } + addError(message, n); + return; + } + // check bounds + for (int i = 0; i < nTypes.length; i++) { + ClassNode nType = nTypes[i].getType(); + ClassNode cnType = cnTypes[i].getType(); + if (!nType.isDerivedFrom(cnType)) { + if (cnType.isInterface() && nType.implementsInterface(cnType)) continue; + addError("The type " + nTypes[i].getName() + + " is not a valid substitute for the bounded parameter <" + + getPrintName(cnTypes[i]) + ">", n); + } + } + } + + private String plural(String orig, int count) { + return "" + count + " " + (count == 1 ? orig : orig + "s"); + } + + private static String getPrintName(GenericsType gt) { + StringBuilder ret = new StringBuilder(gt.getName()); + ClassNode[] upperBounds = gt.getUpperBounds(); + ClassNode lowerBound = gt.getLowerBound(); + if (upperBounds != null) { + if (upperBounds.length != 1 || !"java.lang.Object".equals(getPrintName(upperBounds[0]))) { + ret.append(" extends "); + for (int i = 0; i < upperBounds.length; i++) { + ret.append(getPrintName(upperBounds[i])); + if (i + 1 < upperBounds.length) ret.append(" & "); + } + } + } else if (lowerBound != null) { + ret.append(" super ").append(getPrintName(lowerBound)); + } + return ret.toString(); + } + + private static String getPrintName(ClassNode cn) { + StringBuilder ret = new StringBuilder(cn.getName()); + GenericsType[] gts = cn.getGenericsTypes(); + if (gts != null) { + ret.append("<"); + for (int i = 0; i < gts.length; i++) { + if (i != 0) ret.append(","); + ret.append(getPrintName(gts[i])); + } + ret.append(">"); + } + return ret.toString(); + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/control/HasCleanup.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/control/HasCleanup.java b/src/main/java/org/codehaus/groovy/control/HasCleanup.java new file mode 100644 index 0000000..e7f2365 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/control/HasCleanup.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.codehaus.groovy.control; + +/** + * An interface for things that need to be cleaned up after + * operations complete. + * + * @author Chris Poirier + */ + +public interface HasCleanup +{ + void cleanup(); +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/control/Janitor.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/control/Janitor.java b/src/main/java/org/codehaus/groovy/control/Janitor.java new file mode 100644 index 0000000..8d08ebd --- /dev/null +++ b/src/main/java/org/codehaus/groovy/control/Janitor.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.codehaus.groovy.control; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +/** + * An agent that can be used to defer cleanup operations to + * a later time. Users much implement the HasCleanup interface. + * + * @author Chris Poirier + */ + +public class Janitor implements HasCleanup +{ + private final Set pending = new HashSet(); // All objects pending cleanup + + public void register( HasCleanup object ) + { + pending.add( object ); + } + + public void cleanup() + { + Iterator iterator = pending.iterator(); + while( iterator.hasNext() ) + { + HasCleanup object = (HasCleanup)iterator.next(); + + try { object.cleanup(); } catch( Exception e ) { + // Ignore + } + } + + pending.clear(); + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/control/LabelVerifier.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/control/LabelVerifier.java b/src/main/java/org/codehaus/groovy/control/LabelVerifier.java new file mode 100644 index 0000000..1aea0be --- /dev/null +++ b/src/main/java/org/codehaus/groovy/control/LabelVerifier.java @@ -0,0 +1,174 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.codehaus.groovy.control; + +import org.codehaus.groovy.ast.ClassCodeVisitorSupport; +import org.codehaus.groovy.ast.stmt.BreakStatement; +import org.codehaus.groovy.ast.stmt.ContinueStatement; +import org.codehaus.groovy.ast.stmt.DoWhileStatement; +import org.codehaus.groovy.ast.stmt.ForStatement; +import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.ast.stmt.SwitchStatement; +import org.codehaus.groovy.ast.stmt.WhileStatement; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +/** + * This class checks the handling of labels in the AST + * + * @author Jochen Theodorou + */ +public class LabelVerifier extends ClassCodeVisitorSupport { + + private final SourceUnit source; + private LinkedList visitedLabels; + private LinkedList continueLabels; + private LinkedList breakLabels; + boolean inLoop = false; + boolean inSwitch = false; + + public LabelVerifier(SourceUnit src) { + source = src; + } + + protected SourceUnit getSourceUnit() { + return source; + } + + private void init() { + visitedLabels = new LinkedList(); + continueLabels = new LinkedList(); + breakLabels = new LinkedList(); + inLoop = false; + inSwitch = false; + } + + protected void visitClassCodeContainer(Statement code) { + init(); + super.visitClassCodeContainer(code); + assertNoLabelsMissed(); + } + + public void visitStatement(Statement statement) { + List labels = statement.getStatementLabels(); + + if (labels != null) { + for (String label : labels) { + if (breakLabels != null) { + for (Iterator iter = breakLabels.iterator(); iter.hasNext(); ) { + if (iter.next().getLabel().equals(label)) iter.remove(); + } + } + if (continueLabels != null) { + for (Iterator iter = continueLabels.iterator(); iter.hasNext(); ) { + if (iter.next().getLabel().equals(label)) iter.remove(); + } + } + if (visitedLabels != null) { + visitedLabels.add(label); + } + } + } + + super.visitStatement(statement); + } + + public void visitForLoop(ForStatement forLoop) { + boolean oldInLoop = inLoop; + inLoop = true; + super.visitForLoop(forLoop); + inLoop = oldInLoop; + } + + public void visitDoWhileLoop(DoWhileStatement loop) { + boolean oldInLoop = inLoop; + inLoop = true; + super.visitDoWhileLoop(loop); + inLoop = oldInLoop; + } + + public void visitWhileLoop(WhileStatement loop) { + boolean oldInLoop = inLoop; + inLoop = true; + super.visitWhileLoop(loop); + inLoop = oldInLoop; + } + + public void visitBreakStatement(BreakStatement statement) { + String label = statement.getLabel(); + boolean hasNamedLabel = label != null; + if (!hasNamedLabel && !inLoop && !inSwitch) { + addError("the break statement is only allowed inside loops or switches", statement); + } else if (hasNamedLabel && !inLoop) { + addError("the break statement with named label is only allowed inside loops", statement); + } + if (label != null) { + boolean found = false; + for (String element : visitedLabels) { + if (element.equals(label)) { + found = true; + break; + } + } + if (!found) breakLabels.add(statement); + } + + super.visitBreakStatement(statement); + } + + public void visitContinueStatement(ContinueStatement statement) { + String label = statement.getLabel(); + boolean hasNamedLabel = label != null; + if (!hasNamedLabel && !inLoop) { + addError("the continue statement is only allowed inside loops", statement); + } + if (label != null) { + boolean found = false; + for (String element : visitedLabels) { + if (element.equals(label)) { + found = true; + break; + } + } + if (!found) continueLabels.add(statement); + } + + super.visitContinueStatement(statement); + } + + protected void assertNoLabelsMissed() { + //TODO: report multiple missing labels of the same name only once? + for (ContinueStatement element : continueLabels) { + addError("continue to missing label", element); + } + for (BreakStatement element : breakLabels) { + addError("break to missing label", element); + } + } + + public void visitSwitch(SwitchStatement statement) { + boolean oldInSwitch = inSwitch; + inSwitch = true; + super.visitSwitch(statement); + inSwitch = oldInSwitch; + } + +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/control/MultipleCompilationErrorsException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/control/MultipleCompilationErrorsException.java b/src/main/java/org/codehaus/groovy/control/MultipleCompilationErrorsException.java new file mode 100644 index 0000000..0d7dc3c --- /dev/null +++ b/src/main/java/org/codehaus/groovy/control/MultipleCompilationErrorsException.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.codehaus.groovy.control; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * @author Jochen Theodorou + */ +public class MultipleCompilationErrorsException extends + CompilationFailedException { + + protected ErrorCollector collector; + + public MultipleCompilationErrorsException(ErrorCollector ec) { + super(0, null); + if (ec == null) { + CompilerConfiguration config = super.getUnit() != null ? + super.getUnit().getConfiguration() : + new CompilerConfiguration(); + collector = new ErrorCollector(config); + } else { + collector = ec; + } + } + + public ErrorCollector getErrorCollector() { + return collector; + } + + public String getMessage() { + StringWriter data = new StringWriter(); + PrintWriter writer = new PrintWriter(data); + Janitor janitor = new Janitor(); + + writer.write(super.getMessage()); + writer.println(":"); + try { + collector.write(writer, janitor); + } + finally { + janitor.cleanup(); + } + + return data.toString(); + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/control/OptimizerVisitor.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/control/OptimizerVisitor.java b/src/main/java/org/codehaus/groovy/control/OptimizerVisitor.java new file mode 100644 index 0000000..6bb343b --- /dev/null +++ b/src/main/java/org/codehaus/groovy/control/OptimizerVisitor.java @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.codehaus.groovy.control; + +import org.codehaus.groovy.ast.ClassCodeExpressionTransformer; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.FieldNode; +import org.codehaus.groovy.ast.expr.ClosureExpression; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.objectweb.asm.Opcodes; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveType; + +/** + * Visitor to produce several optimizations: + *
    + *
  • to replace numbered constants with references to static fields
  • + *
  • remove superfluous references to GroovyObject interface
  • + *
+ */ +public class OptimizerVisitor extends ClassCodeExpressionTransformer { + private ClassNode currentClass; + private SourceUnit source; + + // TODO make @CS lookup smarter so that we don't need both these maps + private final Map const2Objects = new HashMap(); + private final Map const2Prims = new HashMap(); + private int index; + private final List missingFields = new LinkedList(); + + public OptimizerVisitor(CompilationUnit cu) { + } + + public void visitClass(ClassNode node, SourceUnit source) { + this.currentClass = node; + this.source = source; + const2Objects.clear(); + const2Prims.clear(); + missingFields.clear(); + index = 0; + super.visitClass(node); + addMissingFields(); + pruneUnneededGroovyObjectInterface(node); + } + + private void pruneUnneededGroovyObjectInterface(ClassNode node) { + ClassNode superClass = node.getSuperClass(); + boolean isSuperGroovy = superClass.isDerivedFromGroovyObject(); + if (isSuperGroovy) { + ClassNode[] interfaces = node.getInterfaces(); + boolean needsFix = false; + for (ClassNode classNode : interfaces) { + if (classNode.equals(ClassHelper.GROOVY_OBJECT_TYPE)) { + needsFix = true; + break; + } + } + if (needsFix) { + List newInterfaces = new ArrayList(interfaces.length); + for (ClassNode classNode : interfaces) { + if (!classNode.equals(ClassHelper.GROOVY_OBJECT_TYPE)) { + newInterfaces.add(classNode); + } + } + node.setInterfaces(newInterfaces.toArray(new ClassNode[newInterfaces.size()])); + } + } + } + + private void addMissingFields() { + for (FieldNode f : missingFields) { + currentClass.addField(f); + } + } + + private void setConstField(ConstantExpression constantExpression) { + final Object n = constantExpression.getValue(); + if (!(n instanceof Number)) return; + if (n instanceof Integer || n instanceof Double) return; + if (n instanceof Long && (0L == (Long) n || 1L == (Long) n)) return; // LCONST_0, LCONST_1 + + boolean isPrimitive = isPrimitiveType(constantExpression.getType()); + FieldNode field = isPrimitive ? const2Prims.get(n) : const2Objects.get(n); + if (field != null) { + constantExpression.setConstantName(field.getName()); + return; + } + String name; + while (true) { + name = "$const$" + index++; + if (currentClass.getDeclaredField(name) == null) break; + } + field = new FieldNode(name, + Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_FINAL, + constantExpression.getType(), + currentClass, + constantExpression); + field.setSynthetic(true); + missingFields.add(field); + constantExpression.setConstantName(field.getName()); + if (isPrimitive) { + const2Prims.put(n, field); + } else { + const2Objects.put(n, field); + } + } + + public Expression transform(Expression exp) { + if (exp == null) return null; + if (!currentClass.isInterface() && exp.getClass() == ConstantExpression.class) { + setConstField((ConstantExpression) exp); + } + return exp.transformExpression(this); + } + + protected SourceUnit getSourceUnit() { + return source; + } + + public void visitClosureExpression(ClosureExpression expression) { + /* + * GROOVY-3339 - do nothing - so that numbers don't get replaced by cached constants in closure classes + */ + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/control/ParserPlugin.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/control/ParserPlugin.java b/src/main/java/org/codehaus/groovy/control/ParserPlugin.java new file mode 100644 index 0000000..9b298d8 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/control/ParserPlugin.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.codehaus.groovy.control; + +import org.codehaus.groovy.ast.ModuleNode; +import org.codehaus.groovy.syntax.ParserException; +import org.codehaus.groovy.syntax.Reduction; + +import java.io.Reader; + +/** + * A simple extension point to allow us to switch between the classic Groovy parser and the new Antlr based parser + * + */ +public interface ParserPlugin { + + Reduction parseCST(SourceUnit sourceUnit, Reader reader) throws CompilationFailedException; + + ModuleNode buildAST(SourceUnit sourceUnit, ClassLoader classLoader, Reduction cst) throws ParserException; +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/control/ParserPluginFactory.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/control/ParserPluginFactory.java b/src/main/java/org/codehaus/groovy/control/ParserPluginFactory.java new file mode 100644 index 0000000..1d7a994 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/control/ParserPluginFactory.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.codehaus.groovy.control; + +import groovy.lang.GroovyRuntimeException; +import org.codehaus.groovy.antlr.AntlrParserPluginFactory; + +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; + +/** + * A factory of parser plugin instances + * + */ +public abstract class ParserPluginFactory { + private static Class ANTLR4_CLASS=null; + + /** + * creates the ANTLR 4 parser + * @return the factory for the parser + */ + public static ParserPluginFactory antlr4() { + if (ANTLR4_CLASS==null) { + String name = "org.apache.groovy.parser.antlr4.Antlr4PluginFactory"; + try { + ANTLR4_CLASS = + Class.forName(name, false, ParserPluginFactory.class.getClassLoader()); + } catch (Exception e) { + throw new GroovyRuntimeException("Could not find or load parser plugin factory for antlr4", e); + } + } + try { + return AccessController.doPrivileged(new PrivilegedExceptionAction() { + public ParserPluginFactory run() throws Exception { + return (ParserPluginFactory) ANTLR4_CLASS.newInstance(); + } + }); + } catch (PrivilegedActionException e) { + throw new GroovyRuntimeException("Could not create instance of parser plugin factory for antlr4", e.getCause()); + } + } + + /** + * creates the ANTLR 2.7 parser + * @return the factory for the parser + */ + public static ParserPluginFactory antlr2() { + return new AntlrParserPluginFactory(); + } + + /** + * creates the ANTLR 2.7 parser. This method was used to switch between the pre JSR + * parser and the new ANTLR 2.7 based parser, but even before Groovy 1.0 this + * method was changed to always return the ANTLR 2.7 parser. + * @param useNewParser - ignored + * @return the ANTLR 2.7 based parser + */ + @Deprecated + public static ParserPluginFactory newInstance(boolean useNewParser) { + return newInstance(); + } + + /** + * creates the ANTLR 2.7 parser. This method was used to switch between the pre JSR + * parser and the new ANTLR 2.7 based parser, but even before Groovy 1.0 this + * method was changed to always return the ANTLR 2.7 parser. + * + * @return the new parser factory. + */ + @Deprecated + public static ParserPluginFactory newInstance() { + return antlr2(); + } + + public abstract ParserPlugin createParserPlugin(); +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/control/ParserVersion.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/control/ParserVersion.java b/src/main/java/org/codehaus/groovy/control/ParserVersion.java new file mode 100644 index 0000000..8440611 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/control/ParserVersion.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.codehaus.groovy.control; + +/** + * Represents the version of a parser + * + * @since 2.6.0 + */ +public enum ParserVersion { + /** + * Before Groovy 2.6.0(including 2.6.0), the default version of parser is v2 + */ + V_2, + + /** + * After Groovy 3.0.0(including 3.0.0), the default version of parser is v4(i.e. the new parser Parrot) + */ + V_4("Parrot"); + + private String name; + + ParserVersion() { + this(null); + } + + ParserVersion(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/control/Phases.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/control/Phases.java b/src/main/java/org/codehaus/groovy/control/Phases.java new file mode 100644 index 0000000..47e421f --- /dev/null +++ b/src/main/java/org/codehaus/groovy/control/Phases.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.codehaus.groovy.control; + + + + +/** + * Compilation phase identifiers. + * + * @author Chris Poirier + */ + +public class Phases +{ + public static final int INITIALIZATION = 1; // Opening of files and such + public static final int PARSING = 2; // Lexing, parsing, and AST building + public static final int CONVERSION = 3; // CST to AST conversion + public static final int SEMANTIC_ANALYSIS = 4; // AST semantic analysis and elucidation + public static final int CANONICALIZATION = 5; // AST completion + public static final int INSTRUCTION_SELECTION = 6; // Class generation, phase 1 + public static final int CLASS_GENERATION = 7; // Class generation, phase 2 + public static final int OUTPUT = 8; // Output of class to disk + public static final int FINALIZATION = 9; // Cleanup + public static final int ALL = 9; // Synonym for full compilation + + public static final String[] descriptions = { + "startup" + , "initialization" + , "parsing" + , "conversion" + , "semantic analysis" + , "canonicalization" + , "instruction selection" + , "class generation" + , "output" + , "cleanup" + }; + + + + /** + * Returns a description of the specified phase. + */ + + public static String getDescription( int phase ) + { + return descriptions[phase]; + } + +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/control/ProcessingUnit.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/control/ProcessingUnit.java b/src/main/java/org/codehaus/groovy/control/ProcessingUnit.java new file mode 100644 index 0000000..d95822d --- /dev/null +++ b/src/main/java/org/codehaus/groovy/control/ProcessingUnit.java @@ -0,0 +1,180 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.codehaus.groovy.control; + +import groovy.lang.GroovyClassLoader; + +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * A base class for data structures that can collect messages and errors + * during processing. + * + * @author Chris Poirier + */ + +public abstract class ProcessingUnit { + + /** + * The current phase + */ + protected int phase; + /** + * Set true if phase is finished + */ + protected boolean phaseComplete; + + /** + * Configuration and other settings that control processing + */ + protected CompilerConfiguration configuration; + + /** + * The ClassLoader to use during processing + */ + protected GroovyClassLoader classLoader; + + /** + * a helper to share errors and report them + */ + protected ErrorCollector errorCollector; + + + /** + * Initialize the ProcessingUnit to the empty state. + */ + + public ProcessingUnit(CompilerConfiguration configuration, GroovyClassLoader classLoader, ErrorCollector er) { + + this.phase = Phases.INITIALIZATION; + this.configuration = configuration; + this.setClassLoader(classLoader); + configure((configuration == null ? new CompilerConfiguration() : configuration)); + if (er==null) er = new ErrorCollector(getConfiguration()); + this.errorCollector = er; + } + + + /** + * Reconfigures the ProcessingUnit. + */ + public void configure(CompilerConfiguration configuration) { + this.configuration = configuration; + } + + + public CompilerConfiguration getConfiguration() { + return configuration; + } + + public void setConfiguration(CompilerConfiguration configuration) { + this.configuration = configuration; + } + + /** + * Returns the class loader in use by this ProcessingUnit. + */ + + public GroovyClassLoader getClassLoader() { + return classLoader; + } + + + /** + * Sets the class loader for use by this ProcessingUnit. + */ + + public void setClassLoader(final GroovyClassLoader loader) { + // Classloaders should only be created inside doPrivileged block + // This code creates a classloader, which needs permission if a security manage is installed. + // If this code might be invoked by code that does not have security permissions, then the classloader creation needs to occur inside a doPrivileged block. + this.classLoader = AccessController.doPrivileged(new PrivilegedAction() { + public GroovyClassLoader run() { + ClassLoader parent = Thread.currentThread().getContextClassLoader(); + if (parent == null) parent = ProcessingUnit.class.getClassLoader(); + return loader == null ? new GroovyClassLoader(parent, configuration) : loader; + } + }); + } + + + /** + * Returns the current phase. + */ + + public int getPhase() { + return this.phase; + } + + + /** + * Returns the description for the current phase. + */ + + public String getPhaseDescription() { + return Phases.getDescription(this.phase); + } + + /** + * Errors found during the compilation should be reported through the ErrorCollector. + * @return + * the ErrorCollector for this ProcessingUnit + */ + public ErrorCollector getErrorCollector() { + return errorCollector; + } + + //--------------------------------------------------------------------------- + // PROCESSING + + + /** + * Marks the current phase complete and processes any + * errors. + */ + + public void completePhase() throws CompilationFailedException { + errorCollector.failIfErrors(); + phaseComplete = true; + } + + + /** + * A synonym for gotoPhase( phase + 1 ). + */ + public void nextPhase() throws CompilationFailedException { + gotoPhase(this.phase + 1); + } + + + /** + * Wraps up any pending operations for the current phase + * and switches to the next phase. + */ + public void gotoPhase(int phase) throws CompilationFailedException { + if (!this.phaseComplete) { + completePhase(); + } + + this.phase = phase; + this.phaseComplete = false; + } + +}