| /* |
| * 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.antlr; |
| |
| import antlr.RecognitionException; |
| import antlr.TokenStreamException; |
| import antlr.TokenStreamRecognitionException; |
| import antlr.collections.AST; |
| import org.codehaus.groovy.GroovyBugError; |
| import org.codehaus.groovy.antlr.parser.GroovyLexer; |
| import org.codehaus.groovy.antlr.parser.GroovyRecognizer; |
| import org.codehaus.groovy.antlr.parser.GroovyTokenTypes; |
| import org.codehaus.groovy.antlr.treewalker.CompositeVisitor; |
| import org.codehaus.groovy.antlr.treewalker.MindMapPrinter; |
| import org.codehaus.groovy.antlr.treewalker.NodeAsHTMLPrinter; |
| import org.codehaus.groovy.antlr.treewalker.PreOrderTraversal; |
| import org.codehaus.groovy.antlr.treewalker.SourceCodeTraversal; |
| import org.codehaus.groovy.antlr.treewalker.SourcePrinter; |
| import org.codehaus.groovy.antlr.treewalker.Visitor; |
| import org.codehaus.groovy.antlr.treewalker.VisitorAdapter; |
| import org.codehaus.groovy.ast.ASTNode; |
| import org.codehaus.groovy.ast.AnnotationNode; |
| import org.codehaus.groovy.ast.ClassHelper; |
| import org.codehaus.groovy.ast.ClassNode; |
| import org.codehaus.groovy.ast.ConstructorNode; |
| import org.codehaus.groovy.ast.EnumConstantClassNode; |
| import org.codehaus.groovy.ast.FieldNode; |
| import org.codehaus.groovy.ast.GenericsType; |
| import org.codehaus.groovy.ast.ImportNode; |
| import org.codehaus.groovy.ast.InnerClassNode; |
| import org.codehaus.groovy.ast.MethodNode; |
| import org.codehaus.groovy.ast.MixinNode; |
| import org.codehaus.groovy.ast.ModuleNode; |
| import org.codehaus.groovy.ast.PackageNode; |
| import org.codehaus.groovy.ast.Parameter; |
| import org.codehaus.groovy.ast.PropertyNode; |
| import org.codehaus.groovy.ast.expr.*; |
| import org.codehaus.groovy.ast.stmt.AssertStatement; |
| import org.codehaus.groovy.ast.stmt.BlockStatement; |
| import org.codehaus.groovy.ast.stmt.BreakStatement; |
| import org.codehaus.groovy.ast.stmt.CaseStatement; |
| import org.codehaus.groovy.ast.stmt.CatchStatement; |
| import org.codehaus.groovy.ast.stmt.ContinueStatement; |
| import org.codehaus.groovy.ast.stmt.EmptyStatement; |
| import org.codehaus.groovy.ast.stmt.ExpressionStatement; |
| import org.codehaus.groovy.ast.stmt.ForStatement; |
| import org.codehaus.groovy.ast.stmt.IfStatement; |
| import org.codehaus.groovy.ast.stmt.ReturnStatement; |
| import org.codehaus.groovy.ast.stmt.Statement; |
| import org.codehaus.groovy.ast.stmt.SwitchStatement; |
| import org.codehaus.groovy.ast.stmt.SynchronizedStatement; |
| import org.codehaus.groovy.ast.stmt.ThrowStatement; |
| import org.codehaus.groovy.ast.stmt.TryCatchStatement; |
| import org.codehaus.groovy.ast.stmt.WhileStatement; |
| import org.codehaus.groovy.control.CompilationFailedException; |
| import org.codehaus.groovy.control.ParserPlugin; |
| import org.codehaus.groovy.control.SourceUnit; |
| import org.codehaus.groovy.control.XStreamUtils; |
| import org.codehaus.groovy.syntax.ASTHelper; |
| import org.codehaus.groovy.syntax.Numbers; |
| import org.codehaus.groovy.syntax.ParserException; |
| import org.codehaus.groovy.syntax.Reduction; |
| import org.codehaus.groovy.syntax.SyntaxException; |
| import org.codehaus.groovy.syntax.Token; |
| import org.codehaus.groovy.syntax.Types; |
| import org.objectweb.asm.Opcodes; |
| |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.PrintStream; |
| import java.io.Reader; |
| import java.security.AccessController; |
| import java.security.PrivilegedAction; |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Set; |
| |
| /** |
| * A parser plugin which adapts the JSR Antlr Parser to the Groovy runtime |
| * |
| * @author <a href="mailto:jstrachan@protique.com">James Strachan</a> |
| */ |
| public class AntlrParserPlugin extends ASTHelper implements ParserPlugin, GroovyTokenTypes { |
| |
| private static class AnonymousInnerClassCarrier extends Expression { |
| ClassNode innerClass; |
| |
| public Expression transformExpression(ExpressionTransformer transformer) { |
| return null; |
| } |
| |
| @Override |
| public void setSourcePosition(final ASTNode node) { |
| super.setSourcePosition(node); |
| innerClass.setSourcePosition(node); |
| } |
| |
| @Override |
| public void setColumnNumber(final int columnNumber) { |
| super.setColumnNumber(columnNumber); |
| innerClass.setColumnNumber(columnNumber); |
| } |
| |
| @Override |
| public void setLineNumber(final int lineNumber) { |
| super.setLineNumber(lineNumber); |
| innerClass.setLineNumber(lineNumber); |
| } |
| |
| @Override |
| public void setLastColumnNumber(final int columnNumber) { |
| super.setLastColumnNumber(columnNumber); |
| innerClass.setLastColumnNumber(columnNumber); |
| } |
| |
| @Override |
| public void setLastLineNumber(final int lineNumber) { |
| super.setLastLineNumber(lineNumber); |
| innerClass.setLastLineNumber(lineNumber); |
| } |
| } |
| |
| protected AST ast; |
| private ClassNode classNode; |
| private MethodNode methodNode; |
| private String[] tokenNames; |
| private int innerClassCounter = 1; |
| private boolean enumConstantBeingDef = false; |
| private boolean forStatementBeingDef = false; |
| private boolean annotationBeingDef = false; |
| private boolean firstParamIsVarArg = false; |
| private boolean firstParam = false; |
| |
| public /*final*/ Reduction parseCST(final SourceUnit sourceUnit, Reader reader) throws CompilationFailedException { |
| final SourceBuffer sourceBuffer = new SourceBuffer(); |
| transformCSTIntoAST(sourceUnit, reader, sourceBuffer); |
| processAST(); |
| return outputAST(sourceUnit, sourceBuffer); |
| } |
| |
| protected void transformCSTIntoAST(SourceUnit sourceUnit, Reader reader, SourceBuffer sourceBuffer) throws CompilationFailedException { |
| ast = null; |
| |
| setController(sourceUnit); |
| |
| // TODO find a way to inject any GroovyLexer/GroovyRecognizer |
| |
| UnicodeEscapingReader unicodeReader = new UnicodeEscapingReader(reader, sourceBuffer); |
| UnicodeLexerSharedInputState inputState = new UnicodeLexerSharedInputState(unicodeReader); |
| GroovyLexer lexer = new GroovyLexer(inputState); |
| unicodeReader.setLexer(lexer); |
| GroovyRecognizer parser = GroovyRecognizer.make(lexer); |
| parser.setSourceBuffer(sourceBuffer); |
| tokenNames = parser.getTokenNames(); |
| parser.setFilename(sourceUnit.getName()); |
| |
| // start parsing at the compilationUnit rule |
| try { |
| parser.compilationUnit(); |
| } |
| catch (TokenStreamRecognitionException tsre) { |
| RecognitionException e = tsre.recog; |
| SyntaxException se = new SyntaxException(e.getMessage(), e, e.getLine(), e.getColumn()); |
| se.setFatal(true); |
| sourceUnit.addError(se); |
| } |
| catch (RecognitionException e) { |
| SyntaxException se = new SyntaxException(e.getMessage(), e, e.getLine(), e.getColumn()); |
| se.setFatal(true); |
| sourceUnit.addError(se); |
| } |
| catch (TokenStreamException e) { |
| sourceUnit.addException(e); |
| } |
| |
| ast = parser.getAST(); |
| } |
| |
| protected void processAST() { |
| AntlrASTProcessor snippets = new AntlrASTProcessSnippets(); |
| ast = snippets.process(ast); |
| } |
| |
| public Reduction outputAST(final SourceUnit sourceUnit, final SourceBuffer sourceBuffer) { |
| AccessController.doPrivileged(new PrivilegedAction() { |
| public Object run() { |
| outputASTInVariousFormsIfNeeded(sourceUnit, sourceBuffer); |
| return null; |
| } |
| }); |
| |
| return null; //new Reduction(Tpken.EOF); |
| } |
| |
| private void outputASTInVariousFormsIfNeeded(SourceUnit sourceUnit, SourceBuffer sourceBuffer) { |
| // straight xstream output of AST |
| String formatProp = System.getProperty("ANTLR.AST".toLowerCase()); // uppercase to hide from jarjar |
| |
| if ("xml".equals(formatProp)) { |
| saveAsXML(sourceUnit.getName(), ast); |
| } |
| |
| // 'pretty printer' output of AST |
| if ("groovy".equals(formatProp)) { |
| try { |
| PrintStream out = new PrintStream(new FileOutputStream(sourceUnit.getName() + ".pretty.groovy")); |
| Visitor visitor = new SourcePrinter(out, tokenNames); |
| AntlrASTProcessor treewalker = new SourceCodeTraversal(visitor); |
| treewalker.process(ast); |
| } catch (FileNotFoundException e) { |
| System.out.println("Cannot create " + sourceUnit.getName() + ".pretty.groovy"); |
| } |
| } |
| |
| // output AST in format suitable for opening in http://freemind.sourceforge.net |
| // which is a really nice way of seeing the AST, folding nodes etc |
| if ("mindmap".equals(formatProp)) { |
| try { |
| PrintStream out = new PrintStream(new FileOutputStream(sourceUnit.getName() + ".mm")); |
| Visitor visitor = new MindMapPrinter(out, tokenNames); |
| AntlrASTProcessor treewalker = new PreOrderTraversal(visitor); |
| treewalker.process(ast); |
| } catch (FileNotFoundException e) { |
| System.out.println("Cannot create " + sourceUnit.getName() + ".mm"); |
| } |
| } |
| |
| // include original line/col info and source code on the mindmap output |
| if ("extendedMindmap".equals(formatProp)) { |
| try { |
| PrintStream out = new PrintStream(new FileOutputStream(sourceUnit.getName() + ".mm")); |
| Visitor visitor = new MindMapPrinter(out, tokenNames, sourceBuffer); |
| AntlrASTProcessor treewalker = new PreOrderTraversal(visitor); |
| treewalker.process(ast); |
| } catch (FileNotFoundException e) { |
| System.out.println("Cannot create " + sourceUnit.getName() + ".mm"); |
| } |
| } |
| |
| // html output of AST |
| if ("html".equals(formatProp)) { |
| try { |
| PrintStream out = new PrintStream(new FileOutputStream(sourceUnit.getName() + ".html")); |
| List<VisitorAdapter> v = new ArrayList<VisitorAdapter>(); |
| v.add(new NodeAsHTMLPrinter(out, tokenNames)); |
| v.add(new SourcePrinter(out, tokenNames)); |
| Visitor visitors = new CompositeVisitor(v); |
| AntlrASTProcessor treewalker = new SourceCodeTraversal(visitors); |
| treewalker.process(ast); |
| } catch (FileNotFoundException e) { |
| System.out.println("Cannot create " + sourceUnit.getName() + ".html"); |
| } |
| } |
| } |
| |
| private static void saveAsXML(String name, AST ast) { |
| XStreamUtils.serialize(name+".antlr", ast); |
| } |
| |
| public ModuleNode buildAST(SourceUnit sourceUnit, ClassLoader classLoader, Reduction cst) throws ParserException { |
| setClassLoader(classLoader); |
| makeModule(); |
| try { |
| convertGroovy(ast); |
| if (output.getStatementBlock().isEmpty() && output.getMethods().isEmpty() && output.getClasses().isEmpty()) { |
| output.addStatement(ReturnStatement.RETURN_NULL_OR_VOID); |
| } |
| |
| // set the script source position |
| |
| ClassNode scriptClassNode = output.getScriptClassDummy(); |
| if (scriptClassNode != null) { |
| List<Statement> statements = output.getStatementBlock().getStatements(); |
| if (!statements.isEmpty()) { |
| Statement firstStatement = statements.get(0); |
| Statement lastStatement = statements.get(statements.size() - 1); |
| |
| scriptClassNode.setSourcePosition(firstStatement); |
| scriptClassNode.setLastColumnNumber(lastStatement.getLastColumnNumber()); |
| scriptClassNode.setLastLineNumber(lastStatement.getLastLineNumber()); |
| } |
| } |
| } |
| catch (ASTRuntimeException e) { |
| throw new ASTParserException(e.getMessage() + ". File: " + sourceUnit.getName(), e); |
| } |
| return output; |
| } |
| |
| /** |
| * Converts the Antlr AST to the Groovy AST |
| */ |
| protected void convertGroovy(AST node) { |
| while (node != null) { |
| int type = node.getType(); |
| switch (type) { |
| case PACKAGE_DEF: |
| packageDef(node); |
| break; |
| |
| case STATIC_IMPORT: |
| case IMPORT: |
| importDef(node); |
| break; |
| |
| case TRAIT_DEF: |
| case CLASS_DEF: |
| classDef(node); |
| break; |
| |
| case INTERFACE_DEF: |
| interfaceDef(node); |
| break; |
| |
| case METHOD_DEF: |
| methodDef(node); |
| break; |
| |
| case ENUM_DEF: |
| enumDef(node); |
| break; |
| |
| case ANNOTATION_DEF: |
| annotationDef(node); |
| break; |
| |
| default: { |
| Statement statement = statement(node); |
| output.addStatement(statement); |
| } |
| } |
| node = node.getNextSibling(); |
| } |
| } |
| |
| // Top level control structures |
| //------------------------------------------------------------------------- |
| |
| protected void packageDef(AST packageDef) { |
| List<AnnotationNode> annotations = new ArrayList<AnnotationNode>(); |
| AST node = packageDef.getFirstChild(); |
| if (isType(ANNOTATIONS, node)) { |
| processAnnotations(annotations, node); |
| node = node.getNextSibling(); |
| } |
| String name = qualifiedName(node); |
| // TODO should we check package node doesn't already exist? conflict? |
| PackageNode packageNode = setPackage(name, annotations); |
| configureAST(packageNode, packageDef); |
| } |
| |
| protected void importDef(AST importNode) { |
| try { |
| // GROOVY-6094 |
| output.putNodeMetaData(ImportNode.class, ImportNode.class); |
| |
| boolean isStatic = importNode.getType() == STATIC_IMPORT; |
| List<AnnotationNode> annotations = new ArrayList<AnnotationNode>(); |
| |
| AST node = importNode.getFirstChild(); |
| if (isType(ANNOTATIONS, node)) { |
| processAnnotations(annotations, node); |
| node = node.getNextSibling(); |
| } |
| |
| String alias = null; |
| if (isType(LITERAL_as, node)) { |
| //import is like "import Foo as Bar" |
| node = node.getFirstChild(); |
| AST aliasNode = node.getNextSibling(); |
| alias = identifier(aliasNode); |
| } |
| |
| if (node.getNumberOfChildren() == 0) { |
| String name = identifier(node); |
| // import is like "import Foo" |
| ClassNode type = ClassHelper.make(name); |
| configureAST(type, importNode); |
| addImport(type, name, alias, annotations); |
| return; |
| } |
| |
| AST packageNode = node.getFirstChild(); |
| String packageName = qualifiedName(packageNode); |
| AST nameNode = packageNode.getNextSibling(); |
| if (isType(STAR, nameNode)) { |
| if (isStatic) { |
| // import is like "import static foo.Bar.*" |
| // packageName is actually a className in this case |
| ClassNode type = ClassHelper.make(packageName); |
| configureAST(type, importNode); |
| addStaticStarImport(type, packageName, annotations); |
| } else { |
| // import is like "import foo.*" |
| addStarImport(packageName, annotations); |
| } |
| |
| if (alias != null) throw new GroovyBugError( |
| "imports like 'import foo.* as Bar' are not " + |
| "supported and should be caught by the grammar"); |
| } else { |
| String name = identifier(nameNode); |
| if (isStatic) { |
| // import is like "import static foo.Bar.method" |
| // packageName is really class name in this case |
| ClassNode type = ClassHelper.make(packageName); |
| configureAST(type, importNode); |
| addStaticImport(type, name, alias, annotations); |
| } else { |
| // import is like "import foo.Bar" |
| ClassNode type = ClassHelper.make(packageName + "." + name); |
| configureAST(type, importNode); |
| addImport(type, name, alias, annotations); |
| } |
| } |
| } finally { |
| // we're using node metadata here in order to fix GROOVY-6094 |
| // without breaking external APIs |
| Object node = output.getNodeMetaData(ImportNode.class); |
| if (node!=null && node!=ImportNode.class) { |
| configureAST((ImportNode)node, importNode); |
| } |
| output.removeNodeMetaData(ImportNode.class); |
| } |
| } |
| |
| private void processAnnotations(List<AnnotationNode> annotations, AST node) { |
| AST child = node.getFirstChild(); |
| while (child != null) { |
| if (isType(ANNOTATION, child)) |
| annotations.add(annotation(child)); |
| child = child.getNextSibling(); |
| } |
| } |
| |
| protected void annotationDef(AST classDef) { |
| List<AnnotationNode> annotations = new ArrayList<AnnotationNode>(); |
| AST node = classDef.getFirstChild(); |
| int modifiers = Opcodes.ACC_PUBLIC; |
| if (isType(MODIFIERS, node)) { |
| modifiers = modifiers(node, annotations, modifiers); |
| checkNoInvalidModifier(classDef, "Annotation Definition", modifiers, Opcodes.ACC_SYNCHRONIZED, "synchronized"); |
| node = node.getNextSibling(); |
| } |
| modifiers |= Opcodes.ACC_ABSTRACT | Opcodes.ACC_INTERFACE | Opcodes.ACC_ANNOTATION; |
| |
| String name = identifier(node); |
| node = node.getNextSibling(); |
| ClassNode superClass = ClassHelper.OBJECT_TYPE; |
| |
| GenericsType[] genericsType = null; |
| if (isType(TYPE_PARAMETERS, node)) { |
| genericsType = makeGenericsType(node); |
| node = node.getNextSibling(); |
| } |
| |
| ClassNode[] interfaces = ClassNode.EMPTY_ARRAY; |
| if (isType(EXTENDS_CLAUSE, node)) { |
| interfaces = interfaces(node); |
| node = node.getNextSibling(); |
| } |
| |
| boolean syntheticPublic = ((modifiers & Opcodes.ACC_SYNTHETIC) != 0); |
| modifiers &= ~Opcodes.ACC_SYNTHETIC; |
| classNode = new ClassNode(dot(getPackageName(), name), modifiers, superClass, interfaces, null); |
| classNode.setSyntheticPublic(syntheticPublic); |
| classNode.addAnnotations(annotations); |
| classNode.setGenericsTypes(genericsType); |
| classNode.addInterface(ClassHelper.Annotation_TYPE); |
| configureAST(classNode, classDef); |
| |
| assertNodeType(OBJBLOCK, node); |
| objectBlock(node); |
| output.addClass(classNode); |
| classNode = null; |
| } |
| |
| protected void interfaceDef(AST classDef) { |
| int oldInnerClassCounter = innerClassCounter; |
| innerInterfaceDef(classDef); |
| classNode = null; |
| innerClassCounter = oldInnerClassCounter; |
| } |
| |
| protected void innerInterfaceDef(AST classDef) { |
| List<AnnotationNode> annotations = new ArrayList<AnnotationNode>(); |
| AST node = classDef.getFirstChild(); |
| int modifiers = Opcodes.ACC_PUBLIC; |
| if (isType(MODIFIERS, node)) { |
| modifiers = modifiers(node, annotations, modifiers); |
| checkNoInvalidModifier(classDef, "Interface", modifiers, Opcodes.ACC_SYNCHRONIZED, "synchronized"); |
| node = node.getNextSibling(); |
| } |
| modifiers |= Opcodes.ACC_ABSTRACT | Opcodes.ACC_INTERFACE; |
| |
| String name = identifier(node); |
| node = node.getNextSibling(); |
| ClassNode superClass = ClassHelper.OBJECT_TYPE; |
| |
| GenericsType[] genericsType = null; |
| if (isType(TYPE_PARAMETERS, node)) { |
| genericsType = makeGenericsType(node); |
| node = node.getNextSibling(); |
| } |
| |
| ClassNode[] interfaces = ClassNode.EMPTY_ARRAY; |
| if (isType(EXTENDS_CLAUSE, node)) { |
| interfaces = interfaces(node); |
| node = node.getNextSibling(); |
| } |
| |
| ClassNode outerClass = classNode; |
| boolean syntheticPublic = ((modifiers & Opcodes.ACC_SYNTHETIC) != 0); |
| modifiers &= ~Opcodes.ACC_SYNTHETIC; |
| if (classNode != null) { |
| name = classNode.getNameWithoutPackage() + "$" + name; |
| String fullName = dot(classNode.getPackageName(), name); |
| classNode = new InnerClassNode(classNode, fullName, modifiers, superClass, interfaces, null); |
| } else { |
| classNode = new ClassNode(dot(getPackageName(), name), modifiers, superClass, interfaces, null); |
| } |
| classNode.setSyntheticPublic(syntheticPublic); |
| classNode.addAnnotations(annotations); |
| classNode.setGenericsTypes(genericsType); |
| configureAST(classNode, classDef); |
| |
| int oldClassCount = innerClassCounter; |
| |
| assertNodeType(OBJBLOCK, node); |
| objectBlock(node); |
| output.addClass(classNode); |
| |
| classNode = outerClass; |
| innerClassCounter = oldClassCount; |
| } |
| |
| protected void classDef(AST classDef) { |
| int oldInnerClassCounter = innerClassCounter; |
| innerClassDef(classDef); |
| classNode = null; |
| innerClassCounter = oldInnerClassCounter; |
| } |
| |
| private ClassNode getClassOrScript(ClassNode node) { |
| if (node != null) return node; |
| return output.getScriptClassDummy(); |
| } |
| |
| protected Expression anonymousInnerClassDef(AST node) { |
| ClassNode oldNode = classNode; |
| ClassNode outerClass = getClassOrScript(oldNode); |
| String fullName = outerClass.getName() + '$' + innerClassCounter; |
| innerClassCounter++; |
| if (enumConstantBeingDef) { |
| classNode = new EnumConstantClassNode(outerClass, fullName, Opcodes.ACC_PUBLIC, ClassHelper.OBJECT_TYPE); |
| } else { |
| classNode = new InnerClassNode(outerClass, fullName, Opcodes.ACC_PUBLIC, ClassHelper.OBJECT_TYPE); |
| } |
| ((InnerClassNode) classNode).setAnonymous(true); |
| classNode.setEnclosingMethod(methodNode); |
| |
| assertNodeType(OBJBLOCK, node); |
| objectBlock(node); |
| output.addClass(classNode); |
| AnonymousInnerClassCarrier ret = new AnonymousInnerClassCarrier(); |
| ret.innerClass = classNode; |
| classNode = oldNode; |
| |
| return ret; |
| } |
| |
| protected void innerClassDef(AST classDef) { |
| List<AnnotationNode> annotations = new ArrayList<AnnotationNode>(); |
| |
| if (isType(TRAIT_DEF, classDef)) { |
| annotations.add(new AnnotationNode(ClassHelper.make("groovy.transform.Trait"))); |
| } |
| |
| AST node = classDef.getFirstChild(); |
| int modifiers = Opcodes.ACC_PUBLIC; |
| if (isType(MODIFIERS, node)) { |
| modifiers = modifiers(node, annotations, modifiers); |
| checkNoInvalidModifier(classDef, "Class", modifiers, Opcodes.ACC_SYNCHRONIZED, "synchronized"); |
| node = node.getNextSibling(); |
| } |
| |
| String name = identifier(node); |
| node = node.getNextSibling(); |
| |
| GenericsType[] genericsType = null; |
| if (isType(TYPE_PARAMETERS, node)) { |
| genericsType = makeGenericsType(node); |
| node = node.getNextSibling(); |
| } |
| |
| ClassNode superClass = null; |
| if (isType(EXTENDS_CLAUSE, node)) { |
| superClass = makeTypeWithArguments(node); |
| node = node.getNextSibling(); |
| } |
| |
| ClassNode[] interfaces = ClassNode.EMPTY_ARRAY; |
| if (isType(IMPLEMENTS_CLAUSE, node)) { |
| interfaces = interfaces(node); |
| node = node.getNextSibling(); |
| } |
| |
| // TODO read mixins |
| MixinNode[] mixins = {}; |
| ClassNode outerClass = classNode; |
| boolean syntheticPublic = ((modifiers & Opcodes.ACC_SYNTHETIC) != 0); |
| modifiers &= ~Opcodes.ACC_SYNTHETIC; |
| if (classNode != null) { |
| name = classNode.getNameWithoutPackage() + "$" + name; |
| String fullName = dot(classNode.getPackageName(), name); |
| if (classNode.isInterface()) { |
| modifiers |= Opcodes.ACC_STATIC; |
| } |
| classNode = new InnerClassNode(classNode, fullName, modifiers, superClass, interfaces, mixins); |
| } else { |
| classNode = new ClassNode(dot(getPackageName(), name), modifiers, superClass, interfaces, mixins); |
| } |
| classNode.addAnnotations(annotations); |
| classNode.setGenericsTypes(genericsType); |
| classNode.setSyntheticPublic(syntheticPublic); |
| configureAST(classNode, classDef); |
| |
| // we put the class already in output to avoid the most inner classes |
| // will be used as first class later in the loader. The first class |
| // there determines what GCL#parseClass for example will return, so we |
| // have here to ensure it won't be the inner class |
| output.addClass(classNode); |
| |
| int oldClassCount = innerClassCounter; |
| |
| assertNodeType(OBJBLOCK, node); |
| objectBlock(node); |
| |
| classNode = outerClass; |
| innerClassCounter = oldClassCount; |
| } |
| |
| protected void objectBlock(AST objectBlock) { |
| for (AST node = objectBlock.getFirstChild(); node != null; node = node.getNextSibling()) { |
| int type = node.getType(); |
| switch (type) { |
| case OBJBLOCK: |
| objectBlock(node); |
| break; |
| |
| case ANNOTATION_FIELD_DEF: |
| case METHOD_DEF: |
| methodDef(node); |
| break; |
| |
| case CTOR_IDENT: |
| constructorDef(node); |
| break; |
| |
| case VARIABLE_DEF: |
| fieldDef(node); |
| break; |
| |
| case STATIC_INIT: |
| staticInit(node); |
| break; |
| |
| case INSTANCE_INIT: |
| objectInit(node); |
| break; |
| |
| case ENUM_DEF: |
| enumDef(node); |
| break; |
| |
| case ENUM_CONSTANT_DEF: |
| enumConstantDef(node); |
| break; |
| |
| case TRAIT_DEF: |
| case CLASS_DEF: |
| innerClassDef(node); |
| break; |
| |
| case INTERFACE_DEF: |
| innerInterfaceDef(node); |
| break; |
| |
| default: |
| unknownAST(node); |
| } |
| } |
| } |
| |
| protected void enumDef(AST enumNode) { |
| assertNodeType(ENUM_DEF, enumNode); |
| List<AnnotationNode> annotations = new ArrayList<AnnotationNode>(); |
| |
| AST node = enumNode.getFirstChild(); |
| int modifiers = Opcodes.ACC_PUBLIC; |
| if (isType(MODIFIERS, node)) { |
| modifiers = modifiers(node, annotations, modifiers); |
| node = node.getNextSibling(); |
| } |
| |
| String name = identifier(node); |
| node = node.getNextSibling(); |
| |
| ClassNode[] interfaces = interfaces(node); |
| node = node.getNextSibling(); |
| |
| boolean syntheticPublic = ((modifiers & Opcodes.ACC_SYNTHETIC) != 0); |
| modifiers &= ~Opcodes.ACC_SYNTHETIC; |
| String enumName = (classNode != null ? name : dot(getPackageName(), name)); |
| ClassNode enumClass = EnumHelper.makeEnumNode(enumName, modifiers, interfaces, classNode); |
| enumClass.setSyntheticPublic(syntheticPublic); |
| ClassNode oldNode = classNode; |
| enumClass.addAnnotations(annotations); |
| classNode = enumClass; |
| configureAST(classNode, enumNode); |
| assertNodeType(OBJBLOCK, node); |
| objectBlock(node); |
| classNode = oldNode; |
| |
| output.addClass(enumClass); |
| } |
| |
| protected void enumConstantDef(AST node) { |
| enumConstantBeingDef = true; |
| assertNodeType(ENUM_CONSTANT_DEF, node); |
| List<AnnotationNode> annotations = new ArrayList<AnnotationNode>(); |
| AST element = node.getFirstChild(); |
| if (isType(ANNOTATIONS, element)) { |
| processAnnotations(annotations, element); |
| element = element.getNextSibling(); |
| } |
| String identifier = identifier(element); |
| Expression init = null; |
| element = element.getNextSibling(); |
| |
| if (element != null) { |
| init = expression(element); |
| ClassNode innerClass; |
| if (element.getNextSibling() == null) { |
| innerClass = getAnonymousInnerClassNode(init); |
| if (innerClass != null) { |
| init = null; |
| } |
| } else { |
| element = element.getNextSibling(); |
| Expression next = expression(element); |
| innerClass = getAnonymousInnerClassNode(next); |
| } |
| |
| if (innerClass != null) { |
| // we have to handle an enum constant with a class overriding |
| // a method in which case we need to configure the inner class |
| innerClass.setSuperClass(classNode.getPlainNodeReference()); |
| innerClass.setModifiers(classNode.getModifiers() | Opcodes.ACC_FINAL); |
| // we use a ClassExpression for transportation to EnumVisitor |
| Expression inner = new ClassExpression(innerClass); |
| if (init == null) { |
| ListExpression le = new ListExpression(); |
| le.addExpression(inner); |
| init = le; |
| } else { |
| if (init instanceof ListExpression) { |
| ((ListExpression) init).addExpression(inner); |
| } else { |
| ListExpression le = new ListExpression(); |
| le.addExpression(init); |
| le.addExpression(inner); |
| init = le; |
| } |
| } |
| // and remove the final modifier from classNode to allow the sub class |
| classNode.setModifiers(classNode.getModifiers() & ~Opcodes.ACC_FINAL); |
| } else if (isType(ELIST, element)) { |
| if (init instanceof ListExpression && !((ListExpression) init).isWrapped()) { |
| ListExpression le = new ListExpression(); |
| le.addExpression(init); |
| init = le; |
| } |
| } |
| } |
| FieldNode enumField = EnumHelper.addEnumConstant(classNode, identifier, init); |
| enumField.addAnnotations(annotations); |
| configureAST(enumField, node); |
| enumConstantBeingDef = false; |
| } |
| |
| protected void throwsList(AST node, List<ClassNode> list) { |
| String name; |
| if (isType(DOT, node)) { |
| name = qualifiedName(node); |
| } else { |
| name = identifier(node); |
| } |
| ClassNode exception = ClassHelper.make(name); |
| configureAST(exception, node); |
| list.add(exception); |
| AST next = node.getNextSibling(); |
| if (next != null) throwsList(next, list); |
| } |
| |
| protected void methodDef(AST methodDef) { |
| MethodNode oldNode = methodNode; |
| List<AnnotationNode> annotations = new ArrayList<AnnotationNode>(); |
| AST node = methodDef.getFirstChild(); |
| |
| GenericsType[] generics = null; |
| if (isType(TYPE_PARAMETERS, node)) { |
| generics = makeGenericsType(node); |
| node = node.getNextSibling(); |
| } |
| |
| int modifiers = Opcodes.ACC_PUBLIC; |
| if (isType(MODIFIERS, node)) { |
| modifiers = modifiers(node, annotations, modifiers); |
| checkNoInvalidModifier(methodDef, "Method", modifiers, Opcodes.ACC_VOLATILE, "volatile"); |
| node = node.getNextSibling(); |
| } |
| |
| if (isAnInterface()) { |
| modifiers |= Opcodes.ACC_ABSTRACT; |
| } |
| |
| ClassNode returnType = null; |
| if (isType(TYPE, node)) { |
| returnType = makeTypeWithArguments(node); |
| node = node.getNextSibling(); |
| } |
| |
| String name = identifier(node); |
| if (classNode != null && !classNode.isAnnotationDefinition()) { |
| if (classNode.getNameWithoutPackage().equals(name)) { |
| if (isAnInterface()) { |
| throw new ASTRuntimeException(methodDef, "Constructor not permitted within an interface."); |
| } |
| throw new ASTRuntimeException(methodDef, "Invalid constructor format. Remove '" + returnType.getName() + |
| "' as the return type if you want a constructor, or use a different name if you want a method."); |
| } |
| } |
| node = node.getNextSibling(); |
| |
| Parameter[] parameters = Parameter.EMPTY_ARRAY; |
| ClassNode[] exceptions = ClassNode.EMPTY_ARRAY; |
| |
| if (classNode == null || !classNode.isAnnotationDefinition()) { |
| |
| assertNodeType(PARAMETERS, node); |
| parameters = parameters(node); |
| if (parameters == null) parameters = Parameter.EMPTY_ARRAY; |
| node = node.getNextSibling(); |
| |
| if (isType(LITERAL_throws, node)) { |
| AST throwsNode = node.getFirstChild(); |
| List<ClassNode> exceptionList = new ArrayList<ClassNode>(); |
| throwsList(throwsNode, exceptionList); |
| exceptions = exceptionList.toArray(exceptions); |
| node = node.getNextSibling(); |
| } |
| } |
| |
| boolean hasAnnotationDefault = false; |
| Statement code = null; |
| boolean syntheticPublic = ((modifiers & Opcodes.ACC_SYNTHETIC) != 0); |
| modifiers &= ~Opcodes.ACC_SYNTHETIC; |
| methodNode = new MethodNode(name, modifiers, returnType, parameters, exceptions, code); |
| if ((modifiers & Opcodes.ACC_ABSTRACT) == 0) { |
| if (node == null) { |
| throw new ASTRuntimeException(methodDef, "You defined a method without body. Try adding a body, or declare it abstract."); |
| } |
| assertNodeType(SLIST, node); |
| code = statementList(node); |
| } else if (node != null && classNode.isAnnotationDefinition()) { |
| code = statement(node); |
| hasAnnotationDefault = true; |
| } else if ((modifiers & Opcodes.ACC_ABSTRACT) > 0) { |
| if (node != null) { |
| throw new ASTRuntimeException(methodDef, "Abstract methods do not define a body."); |
| } |
| } |
| methodNode.setCode(code); |
| methodNode.addAnnotations(annotations); |
| methodNode.setGenericsTypes(generics); |
| methodNode.setAnnotationDefault(hasAnnotationDefault); |
| methodNode.setSyntheticPublic(syntheticPublic); |
| configureAST(methodNode, methodDef); |
| |
| if (classNode != null) { |
| classNode.addMethod(methodNode); |
| } else { |
| output.addMethod(methodNode); |
| } |
| methodNode = oldNode; |
| } |
| |
| private static void checkNoInvalidModifier(AST node, String nodeType, int modifiers, int modifier, String modifierText) { |
| if ((modifiers & modifier) != 0) { |
| throw new ASTRuntimeException(node, nodeType + " has an incorrect modifier '" + modifierText + "'."); |
| } |
| } |
| |
| private boolean isAnInterface() { |
| return classNode != null && (classNode.getModifiers() & Opcodes.ACC_INTERFACE) > 0; |
| } |
| |
| protected void staticInit(AST staticInit) { |
| BlockStatement code = (BlockStatement) statementList(staticInit); |
| classNode.addStaticInitializerStatements(code.getStatements(), false); |
| } |
| |
| protected void objectInit(AST init) { |
| BlockStatement code = (BlockStatement) statementList(init); |
| classNode.addObjectInitializerStatements(code); |
| } |
| |
| protected void constructorDef(AST constructorDef) { |
| List<AnnotationNode> annotations = new ArrayList<AnnotationNode>(); |
| AST node = constructorDef.getFirstChild(); |
| int modifiers = Opcodes.ACC_PUBLIC; |
| if (isType(MODIFIERS, node)) { |
| modifiers = modifiers(node, annotations, modifiers); |
| checkNoInvalidModifier(constructorDef, "Constructor", modifiers, Opcodes.ACC_STATIC, "static"); |
| checkNoInvalidModifier(constructorDef, "Constructor", modifiers, Opcodes.ACC_FINAL, "final"); |
| checkNoInvalidModifier(constructorDef, "Constructor", modifiers, Opcodes.ACC_ABSTRACT, "abstract"); |
| checkNoInvalidModifier(constructorDef, "Constructor", modifiers, Opcodes.ACC_NATIVE, "native"); |
| node = node.getNextSibling(); |
| } |
| |
| assertNodeType(PARAMETERS, node); |
| Parameter[] parameters = parameters(node); |
| if (parameters == null) parameters = Parameter.EMPTY_ARRAY; |
| node = node.getNextSibling(); |
| |
| ClassNode[] exceptions = ClassNode.EMPTY_ARRAY; |
| if (isType(LITERAL_throws, node)) { |
| AST throwsNode = node.getFirstChild(); |
| List<ClassNode> exceptionList = new ArrayList<ClassNode>(); |
| throwsList(throwsNode, exceptionList); |
| exceptions = exceptionList.toArray(exceptions); |
| node = node.getNextSibling(); |
| } |
| |
| assertNodeType(SLIST, node); |
| boolean syntheticPublic = ((modifiers & Opcodes.ACC_SYNTHETIC) != 0); |
| modifiers &= ~Opcodes.ACC_SYNTHETIC; |
| ConstructorNode constructorNode = classNode.addConstructor(modifiers, parameters, exceptions, null); |
| MethodNode oldMethod = methodNode; |
| methodNode = constructorNode; |
| Statement code = statementList(node); |
| methodNode = oldMethod; |
| constructorNode.setCode(code); |
| constructorNode.setSyntheticPublic(syntheticPublic); |
| constructorNode.addAnnotations(annotations); |
| configureAST(constructorNode, constructorDef); |
| } |
| |
| protected void fieldDef(AST fieldDef) { |
| List<AnnotationNode> annotations = new ArrayList<AnnotationNode>(); |
| AST node = fieldDef.getFirstChild(); |
| |
| int modifiers = 0; |
| if (isType(MODIFIERS, node)) { |
| modifiers = modifiers(node, annotations, modifiers); |
| node = node.getNextSibling(); |
| } |
| |
| if (classNode.isInterface()) { |
| modifiers |= Opcodes.ACC_STATIC | Opcodes.ACC_FINAL; |
| if ((modifiers & (Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED)) == 0) { |
| modifiers |= Opcodes.ACC_PUBLIC; |
| } |
| } |
| |
| ClassNode type = null; |
| if (isType(TYPE, node)) { |
| type = makeTypeWithArguments(node); |
| node = node.getNextSibling(); |
| } |
| |
| String name = identifier(node); |
| node = node.getNextSibling(); |
| |
| Expression initialValue = null; |
| if (node != null) { |
| assertNodeType(ASSIGN, node); |
| initialValue = expression(node.getFirstChild()); |
| } |
| |
| if (classNode.isInterface() && initialValue == null && type != null) { |
| initialValue = getDefaultValueForPrimitive(type); |
| } |
| |
| |
| FieldNode fieldNode = new FieldNode(name, modifiers, type, classNode, initialValue); |
| fieldNode.addAnnotations(annotations); |
| configureAST(fieldNode, fieldDef); |
| |
| if (!hasVisibility(modifiers)) { |
| // let's set the modifiers on the field |
| int fieldModifiers = 0; |
| int flags = Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT | Opcodes.ACC_VOLATILE | Opcodes.ACC_FINAL; |
| |
| if (!hasVisibility(modifiers)) { |
| modifiers |= Opcodes.ACC_PUBLIC; |
| fieldModifiers |= Opcodes.ACC_PRIVATE; |
| } |
| |
| // let's pass along any other modifiers we need |
| fieldModifiers |= (modifiers & flags); |
| fieldNode.setModifiers(fieldModifiers); |
| fieldNode.setSynthetic(true); |
| |
| // in the case that there is already a field, we would |
| // like to use that field, instead of the default field |
| // for the property |
| FieldNode storedNode = classNode.getDeclaredField(fieldNode.getName()); |
| if (storedNode != null && !classNode.hasProperty(name)) { |
| fieldNode = storedNode; |
| // we remove it here, because addProperty will add it |
| // again and we want to avoid it showing up multiple |
| // times in the fields list. |
| classNode.getFields().remove(storedNode); |
| } |
| |
| PropertyNode propertyNode = new PropertyNode(fieldNode, modifiers, null, null); |
| configureAST(propertyNode, fieldDef); |
| classNode.addProperty(propertyNode); |
| } else { |
| fieldNode.setModifiers(modifiers); |
| // if there is a property of that name, then a field of that |
| // name already exists, which means this new field here should |
| // be used instead of the field the property originally has. |
| PropertyNode pn = classNode.getProperty(name); |
| if (pn != null && pn.getField().isSynthetic()) { |
| classNode.getFields().remove(pn.getField()); |
| pn.setField(fieldNode); |
| } |
| classNode.addField(fieldNode); |
| } |
| } |
| |
| public static Expression getDefaultValueForPrimitive(ClassNode type) { |
| if (type == ClassHelper.int_TYPE) { |
| return new ConstantExpression(0); |
| } |
| if (type == ClassHelper.long_TYPE) { |
| return new ConstantExpression(0L); |
| } |
| if (type == ClassHelper.double_TYPE) { |
| return new ConstantExpression(0.0); |
| } |
| if (type == ClassHelper.float_TYPE) { |
| return new ConstantExpression(0.0F); |
| } |
| if (type == ClassHelper.boolean_TYPE) { |
| return ConstantExpression.FALSE; |
| } |
| if (type == ClassHelper.short_TYPE) { |
| return new ConstantExpression((short) 0); |
| } |
| if (type == ClassHelper.byte_TYPE) { |
| return new ConstantExpression((byte) 0); |
| } |
| if (type == ClassHelper.char_TYPE) { |
| return new ConstantExpression((char) 0); |
| } |
| return null; |
| } |
| |
| protected ClassNode[] interfaces(AST node) { |
| List<ClassNode> interfaceList = new ArrayList<ClassNode>(); |
| for (AST implementNode = node.getFirstChild(); implementNode != null; implementNode = implementNode.getNextSibling()) { |
| interfaceList.add(makeTypeWithArguments(implementNode)); |
| } |
| ClassNode[] interfaces = ClassNode.EMPTY_ARRAY; |
| if (!interfaceList.isEmpty()) { |
| interfaces = new ClassNode[interfaceList.size()]; |
| interfaceList.toArray(interfaces); |
| } |
| return interfaces; |
| } |
| |
| protected Parameter[] parameters(AST parametersNode) { |
| AST node = parametersNode.getFirstChild(); |
| firstParam = false; |
| firstParamIsVarArg = false; |
| if (node == null) { |
| if (isType(IMPLICIT_PARAMETERS, parametersNode)) return Parameter.EMPTY_ARRAY; |
| return null; |
| } else { |
| List<Parameter> parameters = new ArrayList<Parameter>(); |
| AST firstParameterNode = null; |
| do { |
| firstParam = (firstParameterNode == null); |
| if (firstParameterNode == null) firstParameterNode = node; |
| parameters.add(parameter(node)); |
| node = node.getNextSibling(); |
| } |
| while (node != null); |
| |
| verifyParameters(parameters, firstParameterNode); |
| |
| Parameter[] answer = new Parameter[parameters.size()]; |
| parameters.toArray(answer); |
| return answer; |
| } |
| } |
| |
| private void verifyParameters(List<Parameter> parameters, AST firstParameterNode) { |
| if (parameters.size() <= 1) return; |
| |
| Parameter first = parameters.get(0); |
| if (firstParamIsVarArg) { |
| throw new ASTRuntimeException(firstParameterNode, "The var-arg parameter " + first.getName() + " must be the last parameter."); |
| } |
| } |
| |
| protected Parameter parameter(AST paramNode) { |
| List<AnnotationNode> annotations = new ArrayList<AnnotationNode>(); |
| boolean variableParameterDef = isType(VARIABLE_PARAMETER_DEF, paramNode); |
| AST node = paramNode.getFirstChild(); |
| |
| int modifiers = 0; |
| if (isType(MODIFIERS, node)) { |
| modifiers = modifiers(node, annotations, modifiers); |
| node = node.getNextSibling(); |
| } |
| |
| ClassNode type = ClassHelper.DYNAMIC_TYPE; |
| if (isType(TYPE, node)) { |
| type = makeTypeWithArguments(node); |
| if (variableParameterDef) type = type.makeArray(); |
| node = node.getNextSibling(); |
| } |
| |
| String name = identifier(node); |
| node = node.getNextSibling(); |
| |
| VariableExpression leftExpression = new VariableExpression(name, type); |
| leftExpression.setModifiers(modifiers); |
| configureAST(leftExpression, paramNode); |
| |
| Parameter parameter = null; |
| if (node != null) { |
| assertNodeType(ASSIGN, node); |
| Expression rightExpression = expression(node.getFirstChild()); |
| if (isAnInterface()) { |
| throw new ASTRuntimeException(node, "Cannot specify default value for method parameter '" + name + " = " + rightExpression.getText() + "' inside an interface"); |
| } |
| parameter = new Parameter(type, name, rightExpression); |
| } else |
| parameter = new Parameter(type, name); |
| |
| if (firstParam) firstParamIsVarArg = variableParameterDef; |
| |
| configureAST(parameter, paramNode); |
| parameter.addAnnotations(annotations); |
| parameter.setModifiers(modifiers); |
| return parameter; |
| } |
| |
| protected int modifiers(AST modifierNode, List<AnnotationNode> annotations, int defaultModifiers) { |
| assertNodeType(MODIFIERS, modifierNode); |
| |
| boolean access = false; |
| int answer = 0; |
| |
| for (AST node = modifierNode.getFirstChild(); node != null; node = node.getNextSibling()) { |
| int type = node.getType(); |
| switch (type) { |
| case STATIC_IMPORT: |
| // ignore |
| break; |
| |
| // annotations |
| case ANNOTATION: |
| annotations.add(annotation(node)); |
| break; |
| |
| // core access scope modifiers |
| case LITERAL_private: |
| answer = setModifierBit(node, answer, Opcodes.ACC_PRIVATE); |
| access = setAccessTrue(node, access); |
| break; |
| |
| case LITERAL_protected: |
| answer = setModifierBit(node, answer, Opcodes.ACC_PROTECTED); |
| access = setAccessTrue(node, access); |
| break; |
| |
| case LITERAL_public: |
| answer = setModifierBit(node, answer, Opcodes.ACC_PUBLIC); |
| access = setAccessTrue(node, access); |
| break; |
| |
| // other modifiers |
| case ABSTRACT: |
| answer = setModifierBit(node, answer, Opcodes.ACC_ABSTRACT); |
| break; |
| |
| case FINAL: |
| answer = setModifierBit(node, answer, Opcodes.ACC_FINAL); |
| break; |
| |
| case LITERAL_native: |
| answer = setModifierBit(node, answer, Opcodes.ACC_NATIVE); |
| break; |
| |
| case LITERAL_static: |
| answer = setModifierBit(node, answer, Opcodes.ACC_STATIC); |
| break; |
| |
| case STRICTFP: |
| answer = setModifierBit(node, answer, Opcodes.ACC_STRICT); |
| break; |
| |
| case LITERAL_synchronized: |
| answer = setModifierBit(node, answer, Opcodes.ACC_SYNCHRONIZED); |
| break; |
| |
| case LITERAL_transient: |
| answer = setModifierBit(node, answer, Opcodes.ACC_TRANSIENT); |
| break; |
| |
| case LITERAL_volatile: |
| answer = setModifierBit(node, answer, Opcodes.ACC_VOLATILE); |
| break; |
| |
| default: |
| unknownAST(node); |
| } |
| } |
| if (!access) { |
| answer |= defaultModifiers; |
| // ACC_SYNTHETIC isn't used here, use it as a special flag |
| if (defaultModifiers == Opcodes.ACC_PUBLIC) answer |= Opcodes.ACC_SYNTHETIC; |
| } |
| return answer; |
| } |
| |
| protected boolean setAccessTrue(AST node, boolean access) { |
| if (!access) { |
| return true; |
| } else { |
| throw new ASTRuntimeException(node, "Cannot specify modifier: " + node.getText() + " when access scope has already been defined"); |
| } |
| } |
| |
| protected int setModifierBit(AST node, int answer, int bit) { |
| if ((answer & bit) != 0) { |
| throw new ASTRuntimeException(node, "Cannot repeat modifier: " + node.getText()); |
| } |
| return answer | bit; |
| } |
| |
| protected AnnotationNode annotation(AST annotationNode) { |
| annotationBeingDef = true; |
| AST node = annotationNode.getFirstChild(); |
| String name = qualifiedName(node); |
| AnnotationNode annotatedNode = new AnnotationNode(ClassHelper.make(name)); |
| configureAST(annotatedNode, annotationNode); |
| while (true) { |
| node = node.getNextSibling(); |
| if (isType(ANNOTATION_MEMBER_VALUE_PAIR, node)) { |
| AST memberNode = node.getFirstChild(); |
| String param = identifier(memberNode); |
| Expression expression = expression(memberNode.getNextSibling()); |
| if (annotatedNode.getMember(param) != null) { |
| throw new ASTRuntimeException(memberNode, "Annotation member '" + param + "' has already been associated with a value"); |
| } |
| annotatedNode.setMember(param, expression); |
| } else { |
| break; |
| } |
| } |
| annotationBeingDef = false; |
| return annotatedNode; |
| } |
| |
| |
| // Statements |
| //------------------------------------------------------------------------- |
| |
| protected Statement statement(AST node) { |
| Statement statement = null; |
| int type = node.getType(); |
| switch (type) { |
| case SLIST: |
| case LITERAL_finally: |
| statement = statementList(node); |
| break; |
| |
| case METHOD_CALL: |
| statement = methodCall(node); |
| break; |
| |
| case VARIABLE_DEF: |
| statement = variableDef(node); |
| break; |
| |
| case LABELED_STAT: |
| return labelledStatement(node); |
| |
| case LITERAL_assert: |
| statement = assertStatement(node); |
| break; |
| |
| case LITERAL_break: |
| statement = breakStatement(node); |
| break; |
| |
| case LITERAL_continue: |
| statement = continueStatement(node); |
| break; |
| |
| case LITERAL_if: |
| statement = ifStatement(node); |
| break; |
| |
| case LITERAL_for: |
| statement = forStatement(node); |
| break; |
| |
| case LITERAL_return: |
| statement = returnStatement(node); |
| break; |
| |
| case LITERAL_synchronized: |
| statement = synchronizedStatement(node); |
| break; |
| |
| case LITERAL_switch: |
| statement = switchStatement(node); |
| break; |
| |
| case LITERAL_try: |
| statement = tryStatement(node); |
| break; |
| |
| case LITERAL_throw: |
| statement = throwStatement(node); |
| break; |
| |
| case LITERAL_while: |
| statement = whileStatement(node); |
| break; |
| |
| default: |
| statement = new ExpressionStatement(expression(node)); |
| } |
| if (statement != null) { |
| configureAST(statement, node); |
| } |
| return statement; |
| } |
| |
| protected Statement statementList(AST code) { |
| return statementListNoChild(code.getFirstChild(), code); |
| } |
| |
| protected Statement statementListNoChild(AST node, AST alternativeConfigureNode) { |
| BlockStatement block = new BlockStatement(); |
| // alternativeConfigureNode is used only to set the source position |
| if (node != null) { |
| configureAST(block, node); |
| } else { |
| configureAST(block, alternativeConfigureNode); |
| } |
| for (; node != null; node = node.getNextSibling()) { |
| block.addStatement(statement(node)); |
| } |
| return block; |
| } |
| |
| protected Statement assertStatement(AST assertNode) { |
| AST node = assertNode.getFirstChild(); |
| BooleanExpression booleanExpression = booleanExpression(node); |
| Expression messageExpression = null; |
| |
| node = node.getNextSibling(); |
| if (node != null) { |
| messageExpression = expression(node); |
| } else { |
| messageExpression = ConstantExpression.NULL; |
| } |
| AssertStatement assertStatement = new AssertStatement(booleanExpression, messageExpression); |
| configureAST(assertStatement, assertNode); |
| return assertStatement; |
| } |
| |
| protected Statement breakStatement(AST node) { |
| BreakStatement breakStatement = new BreakStatement(label(node)); |
| configureAST(breakStatement, node); |
| return breakStatement; |
| } |
| |
| protected Statement continueStatement(AST node) { |
| ContinueStatement continueStatement = new ContinueStatement(label(node)); |
| configureAST(continueStatement, node); |
| return continueStatement; |
| } |
| |
| protected Statement forStatement(AST forNode) { |
| AST inNode = forNode.getFirstChild(); |
| Expression collectionExpression; |
| Parameter forParameter; |
| if (isType(CLOSURE_LIST, inNode)) { |
| forStatementBeingDef = true; |
| ClosureListExpression clist = closureListExpression(inNode); |
| forStatementBeingDef = false; |
| int size = clist.getExpressions().size(); |
| if (size != 3) { |
| throw new ASTRuntimeException(inNode, "3 expressions are required for the classic for loop, you gave " + size); |
| } |
| collectionExpression = clist; |
| forParameter = ForStatement.FOR_LOOP_DUMMY; |
| } else { |
| AST variableNode = inNode.getFirstChild(); |
| AST collectionNode = variableNode.getNextSibling(); |
| |
| ClassNode type = ClassHelper.OBJECT_TYPE; |
| if (isType(VARIABLE_DEF, variableNode)) { |
| AST node = variableNode.getFirstChild(); |
| // skip the final modifier if it's present |
| if (isType(MODIFIERS, node)) { |
| int modifiersMask = modifiers(node, new ArrayList<AnnotationNode>(), 0); |
| // only final modifier allowed |
| if ((modifiersMask & ~Opcodes.ACC_FINAL) != 0) { |
| throw new ASTRuntimeException(node, "Only the 'final' modifier is allowed in front of the for loop variable."); |
| } |
| node = node.getNextSibling(); |
| } |
| type = makeTypeWithArguments(node); |
| |
| variableNode = node.getNextSibling(); |
| } |
| String variable = identifier(variableNode); |
| |
| collectionExpression = expression(collectionNode); |
| forParameter = new Parameter(type, variable); |
| configureAST(forParameter, variableNode); |
| } |
| |
| final AST node = inNode.getNextSibling(); |
| Statement block; |
| if (isType(SEMI, node)) { |
| block = EmptyStatement.INSTANCE; |
| } else { |
| block = statement(node); |
| } |
| ForStatement forStatement = new ForStatement(forParameter, collectionExpression, block); |
| configureAST(forStatement, forNode); |
| return forStatement; |
| } |
| |
| protected Statement ifStatement(AST ifNode) { |
| AST node = ifNode.getFirstChild(); |
| assertNodeType(EXPR, node); |
| BooleanExpression booleanExpression = booleanExpression(node); |
| |
| node = node.getNextSibling(); |
| Statement ifBlock = statement(node); |
| |
| Statement elseBlock = EmptyStatement.INSTANCE; |
| node = node.getNextSibling(); |
| if (node != null) { |
| elseBlock = statement(node); |
| } |
| IfStatement ifStatement = new IfStatement(booleanExpression, ifBlock, elseBlock); |
| configureAST(ifStatement, ifNode); |
| return ifStatement; |
| } |
| |
| protected Statement labelledStatement(AST labelNode) { |
| AST node = labelNode.getFirstChild(); |
| String label = identifier(node); |
| Statement statement = statement(node.getNextSibling()); |
| statement.addStatementLabel(label); |
| return statement; |
| } |
| |
| protected Statement methodCall(AST code) { |
| Expression expression = methodCallExpression(code); |
| ExpressionStatement expressionStatement = new ExpressionStatement(expression); |
| configureAST(expressionStatement, code); |
| return expressionStatement; |
| } |
| |
| protected Expression declarationExpression(AST variableDef) { |
| AST node = variableDef.getFirstChild(); |
| ClassNode type = null; |
| List<AnnotationNode> annotations = new ArrayList<AnnotationNode>(); |
| int modifiers = 0; |
| if (isType(MODIFIERS, node)) { |
| // force check of modifier conflicts |
| modifiers = modifiers(node, annotations, 0); |
| node = node.getNextSibling(); |
| } |
| if (isType(TYPE, node)) { |
| type = makeTypeWithArguments(node); |
| node = node.getNextSibling(); |
| } |
| |
| Expression leftExpression; |
| Expression rightExpression = EmptyExpression.INSTANCE; |
| AST right; |
| |
| if (isType(ASSIGN, node)) { |
| node = node.getFirstChild(); |
| AST left = node.getFirstChild(); |
| ArgumentListExpression alist = new ArgumentListExpression(); |
| for (AST varDef = left; varDef != null; varDef = varDef.getNextSibling()) { |
| assertNodeType(VARIABLE_DEF, varDef); |
| DeclarationExpression de = (DeclarationExpression) declarationExpression(varDef); |
| alist.addExpression(de.getVariableExpression()); |
| } |
| leftExpression = alist; |
| right = node.getNextSibling(); |
| if (right != null) rightExpression = expression(right); |
| } else { |
| String name = identifier(node); |
| VariableExpression ve = new VariableExpression(name, type); |
| ve.setModifiers(modifiers); |
| leftExpression = ve; |
| |
| right = node.getNextSibling(); |
| if (right != null) { |
| assertNodeType(ASSIGN, right); |
| rightExpression = expression(right.getFirstChild()); |
| } |
| } |
| |
| configureAST(leftExpression, node); |
| |
| Token token = makeToken(Types.ASSIGN, variableDef); |
| DeclarationExpression expression = new DeclarationExpression(leftExpression, token, rightExpression); |
| expression.addAnnotations(annotations); |
| configureAST(expression, variableDef); |
| ExpressionStatement expressionStatement = new ExpressionStatement(expression); |
| configureAST(expressionStatement, variableDef); |
| return expression; |
| } |
| |
| protected Statement variableDef(AST variableDef) { |
| ExpressionStatement expressionStatement = new ExpressionStatement(declarationExpression(variableDef)); |
| configureAST(expressionStatement, variableDef); |
| return expressionStatement; |
| } |
| |
| protected Statement returnStatement(AST node) { |
| AST exprNode = node.getFirstChild(); |
| |
| // This will pick up incorrect sibling node if 'node' is a plain 'return' |
| // |
| //if (exprNode == null) { |
| // exprNode = node.getNextSibling(); |
| //} |
| Expression expression = exprNode == null ? ConstantExpression.NULL : expression(exprNode); |
| ReturnStatement returnStatement = new ReturnStatement(expression); |
| configureAST(returnStatement, node); |
| return returnStatement; |
| } |
| |
| protected Statement switchStatement(AST switchNode) { |
| AST node = switchNode.getFirstChild(); |
| Expression expression = expression(node); |
| Statement defaultStatement = EmptyStatement.INSTANCE; |
| |
| List list = new ArrayList(); |
| for (node = node.getNextSibling(); isType(CASE_GROUP, node); node = node.getNextSibling()) { |
| Statement tmpDefaultStatement; |
| AST child = node.getFirstChild(); |
| if (isType(LITERAL_case, child)) { |
| List cases = new LinkedList(); |
| // default statement can be grouped with previous case |
| tmpDefaultStatement = caseStatements(child, cases); |
| list.addAll(cases); |
| } else { |
| tmpDefaultStatement = statement(child.getNextSibling()); |
| } |
| if (tmpDefaultStatement != EmptyStatement.INSTANCE) { |
| if (defaultStatement == EmptyStatement.INSTANCE) { |
| defaultStatement = tmpDefaultStatement; |
| } else { |
| throw new ASTRuntimeException(switchNode, "The default case is already defined."); |
| } |
| } |
| } |
| if (node != null) { |
| unknownAST(node); |
| } |
| SwitchStatement switchStatement = new SwitchStatement(expression, list, defaultStatement); |
| configureAST(switchStatement, switchNode); |
| return switchStatement; |
| } |
| |
| protected Statement caseStatements(AST node, List cases) { |
| List<Expression> expressions = new LinkedList<Expression>(); |
| Statement statement = EmptyStatement.INSTANCE; |
| Statement defaultStatement = EmptyStatement.INSTANCE; |
| AST nextSibling = node; |
| do { |
| Expression expression = expression(nextSibling.getFirstChild()); |
| expressions.add(expression); |
| nextSibling = nextSibling.getNextSibling(); |
| } while (isType(LITERAL_case, nextSibling)); |
| if (nextSibling != null) { |
| if (isType(LITERAL_default, nextSibling)) { |
| defaultStatement = statement(nextSibling.getNextSibling()); |
| statement = EmptyStatement.INSTANCE; |
| } else { |
| statement = statement(nextSibling); |
| } |
| } |
| Iterator iterator = expressions.iterator(); |
| while (iterator.hasNext()) { |
| Expression expr = (Expression) iterator.next(); |
| Statement stmt; |
| if (iterator.hasNext()) { |
| stmt = new CaseStatement(expr, EmptyStatement.INSTANCE); |
| } else { |
| stmt = new CaseStatement(expr, statement); |
| } |
| configureAST(stmt, node); |
| cases.add(stmt); |
| } |
| return defaultStatement; |
| } |
| |
| protected Statement synchronizedStatement(AST syncNode) { |
| AST node = syncNode.getFirstChild(); |
| Expression expression = expression(node); |
| Statement code = statement(node.getNextSibling()); |
| SynchronizedStatement synchronizedStatement = new SynchronizedStatement(expression, code); |
| configureAST(synchronizedStatement, syncNode); |
| return synchronizedStatement; |
| } |
| |
| protected Statement throwStatement(AST node) { |
| AST expressionNode = node.getFirstChild(); |
| if (expressionNode == null) { |
| expressionNode = node.getNextSibling(); |
| } |
| if (expressionNode == null) { |
| throw new ASTRuntimeException(node, "No expression available"); |
| } |
| ThrowStatement throwStatement = new ThrowStatement(expression(expressionNode)); |
| configureAST(throwStatement, node); |
| return throwStatement; |
| } |
| |
| protected Statement tryStatement(AST tryStatementNode) { |
| AST tryNode = tryStatementNode.getFirstChild(); |
| Statement tryStatement = statement(tryNode); |
| Statement finallyStatement = EmptyStatement.INSTANCE; |
| AST node = tryNode.getNextSibling(); |
| |
| // let's do the catch nodes |
| List<CatchStatement> catches = new ArrayList<CatchStatement>(); |
| for (; node != null && isType(LITERAL_catch, node); node = node.getNextSibling()) { |
| final List<CatchStatement> catchStatements = catchStatement(node); |
| catches.addAll(catchStatements); |
| } |
| |
| if (isType(LITERAL_finally, node)) { |
| finallyStatement = statement(node); |
| node = node.getNextSibling(); |
| } |
| |
| if (finallyStatement instanceof EmptyStatement && catches.isEmpty()) { |
| throw new ASTRuntimeException(tryStatementNode, "A try statement must have at least one catch or finally block."); |
| } |
| |
| TryCatchStatement tryCatchStatement = new TryCatchStatement(tryStatement, finallyStatement); |
| configureAST(tryCatchStatement, tryStatementNode); |
| for (CatchStatement statement : catches) { |
| tryCatchStatement.addCatch(statement); |
| } |
| return tryCatchStatement; |
| } |
| |
| protected List<CatchStatement> catchStatement(AST catchNode) { |
| AST node = catchNode.getFirstChild(); |
| List<CatchStatement> catches = new LinkedList<CatchStatement>(); |
| Statement code = statement(node.getNextSibling()); |
| if (MULTICATCH == node.getType()) { |
| AST variableNode = node.getNextSibling(); |
| final AST multicatches = node.getFirstChild(); |
| if (multicatches.getType() != MULTICATCH_TYPES) { |
| // catch (e) |
| // catch (def e) |
| String variable = identifier(multicatches); |
| Parameter catchParameter = new Parameter(ClassHelper.DYNAMIC_TYPE, variable); |
| CatchStatement answer = new CatchStatement(catchParameter, code); |
| configureAST(answer, catchNode); |
| catches.add(answer); |
| } else { |
| // catch (Exception e) |
| // catch (Exception1 | Exception2 e) |
| AST exceptionNodes = multicatches.getFirstChild(); |
| String variable = identifier(multicatches.getNextSibling()); |
| while (exceptionNodes != null) { |
| ClassNode exceptionType = buildName(exceptionNodes); |
| Parameter catchParameter = new Parameter(exceptionType, variable); |
| CatchStatement answer = new CatchStatement(catchParameter, code); |
| configureAST(answer, catchNode); |
| catches.add(answer); |
| exceptionNodes = exceptionNodes.getNextSibling(); |
| } |
| } |
| } |
| return catches; |
| } |
| |
| protected Statement whileStatement(AST whileNode) { |
| AST node = whileNode.getFirstChild(); |
| assertNodeType(EXPR, node); |
| // TODO remove this once we support declarations in the while condition |
| if (isType(VARIABLE_DEF, node.getFirstChild())) { |
| throw new ASTRuntimeException(whileNode, |
| "While loop condition contains a declaration; this is currently unsupported."); |
| } |
| BooleanExpression booleanExpression = booleanExpression(node); |
| |
| node = node.getNextSibling(); |
| Statement block; |
| if (isType(SEMI, node)) { |
| block = EmptyStatement.INSTANCE; |
| } else { |
| block = statement(node); |
| } |
| WhileStatement whileStatement = new WhileStatement(booleanExpression, block); |
| configureAST(whileStatement, whileNode); |
| return whileStatement; |
| } |
| |
| |
| // Expressions |
| //------------------------------------------------------------------------- |
| |
| protected Expression expression(AST node) { |
| return expression(node, false); |
| } |
| |
| protected Expression expression(AST node, boolean convertToConstant) { |
| Expression expression = expressionSwitch(node); |
| if (convertToConstant && expression instanceof VariableExpression) { |
| // a method name can never be a VariableExpression, so it must converted |
| // to a ConstantExpression then. This is needed as the expression |
| // method doesn't know we want a ConstantExpression instead of a |
| // VariableExpression |
| VariableExpression ve = (VariableExpression) expression; |
| if (!ve.isThisExpression() && !ve.isSuperExpression()) { |
| expression = new ConstantExpression(ve.getName()); |
| } |
| } |
| configureAST(expression, node); |
| return expression; |
| } |
| |
| protected Expression expressionSwitch(AST node) { |
| int type = node.getType(); |
| switch (type) { |
| case EXPR: |
| return expression(node.getFirstChild()); |
| |
| case ELIST: |
| return expressionList(node); |
| |
| case SLIST: |
| return blockExpression(node); |
| |
| case CLOSABLE_BLOCK: |
| return closureExpression(node); |
| |
| case SUPER_CTOR_CALL: |
| return specialConstructorCallExpression(node, ClassNode.SUPER); |
| |
| case METHOD_CALL: |
| return methodCallExpression(node); |
| |
| case LITERAL_new: |
| return constructorCallExpression(node); |
| |
| case CTOR_CALL: |
| return specialConstructorCallExpression(node, ClassNode.THIS); |
| |
| case QUESTION: |
| case ELVIS_OPERATOR: |
| return ternaryExpression(node); |
| |
| case OPTIONAL_DOT: |
| case SPREAD_DOT: |
| case DOT: |
| return dotExpression(node); |
| |
| case IDENT: |
| case LITERAL_boolean: |
| case LITERAL_byte: |
| case LITERAL_char: |
| case LITERAL_double: |
| case LITERAL_float: |
| case LITERAL_int: |
| case LITERAL_long: |
| case LITERAL_short: |
| case LITERAL_void: |
| case LITERAL_this: |
| case LITERAL_super: |
| return variableExpression(node); |
| |
| case LIST_CONSTRUCTOR: |
| return listExpression(node); |
| |
| case MAP_CONSTRUCTOR: |
| return mapExpression(node); |
| |
| case LABELED_ARG: |
| return mapEntryExpression(node); |
| |
| case SPREAD_ARG: |
| return spreadExpression(node); |
| |
| case SPREAD_MAP_ARG: |
| return spreadMapExpression(node); |
| |
| // commented out of groovy.g due to non determinisms |
| //case MEMBER_POINTER_DEFAULT: |
| // return defaultMethodPointerExpression(node); |
| |
| case MEMBER_POINTER: |
| return methodPointerExpression(node); |
| |
| case INDEX_OP: |
| return indexExpression(node); |
| |
| case LITERAL_instanceof: |
| return instanceofExpression(node); |
| |
| case LITERAL_as: |
| return asExpression(node); |
| |
| case TYPECAST: |
| return castExpression(node); |
| |
| // literals |
| |
| case LITERAL_true: |
| return literalExpression(node, Boolean.TRUE); |
| case LITERAL_false: |
| return literalExpression(node, Boolean.FALSE); |
| case LITERAL_null: |
| return literalExpression(node, null); |
| case STRING_LITERAL: |
| return literalExpression(node, node.getText()); |
| |
| case STRING_CONSTRUCTOR: |
| return gstring(node); |
| |
| case NUM_DOUBLE: |
| case NUM_FLOAT: |
| case NUM_BIG_DECIMAL: |
| return decimalExpression(node); |
| |
| case NUM_BIG_INT: |
| case NUM_INT: |
| case NUM_LONG: |
| return integerExpression(node); |
| |
| // Unary expressions |
| case LNOT: |
| NotExpression notExpression = new NotExpression(expression(node.getFirstChild())); |
| configureAST(notExpression, node); |
| return notExpression; |
| |
| case UNARY_MINUS: |
| return unaryMinusExpression(node); |
| |
| case BNOT: |
| BitwiseNegationExpression bitwiseNegationExpression = new BitwiseNegationExpression(expression(node.getFirstChild())); |
| configureAST(bitwiseNegationExpression, node); |
| return bitwiseNegationExpression; |
| |
| case UNARY_PLUS: |
| return unaryPlusExpression(node); |
| |
| // Prefix expressions |
| case INC: |
| return prefixExpression(node, Types.PLUS_PLUS); |
| |
| case DEC: |
| return prefixExpression(node, Types.MINUS_MINUS); |
| |
| // Postfix expressions |
| case POST_INC: |
| return postfixExpression(node, Types.PLUS_PLUS); |
| |
| case POST_DEC: |
| return postfixExpression(node, Types.MINUS_MINUS); |
| |
| |
| // Binary expressions |
| |
| case ASSIGN: |
| return binaryExpression(Types.ASSIGN, node); |
| |
| case EQUAL: |
| return binaryExpression(Types.COMPARE_EQUAL, node); |
| |
| case IDENTICAL: |
| return binaryExpression(Types.COMPARE_IDENTICAL, node); |
| |
| case NOT_EQUAL: |
| return binaryExpression(Types.COMPARE_NOT_EQUAL, node); |
| |
| case NOT_IDENTICAL: |
| return binaryExpression(Types.COMPARE_NOT_IDENTICAL, node); |
| |
| case COMPARE_TO: |
| return binaryExpression(Types.COMPARE_TO, node); |
| |
| case LE: |
| return binaryExpression(Types.COMPARE_LESS_THAN_EQUAL, node); |
| |
| case LT: |
| return binaryExpression(Types.COMPARE_LESS_THAN, node); |
| |
| case GT: |
| return binaryExpression(Types.COMPARE_GREATER_THAN, node); |
| |
| case GE: |
| return binaryExpression(Types.COMPARE_GREATER_THAN_EQUAL, node); |
| |
| /** |
| * TODO treble equal? |
| return binaryExpression(Types.COMPARE_IDENTICAL, node); |
| |
| case ???: |
| return binaryExpression(Types.LOGICAL_AND_EQUAL, node); |
| |
| case ???: |
| return binaryExpression(Types.LOGICAL_OR_EQUAL, node); |
| |
| */ |
| |
| case LAND: |
| return binaryExpression(Types.LOGICAL_AND, node); |
| |
| case LOR: |
| return binaryExpression(Types.LOGICAL_OR, node); |
| |
| case BAND: |
| return binaryExpression(Types.BITWISE_AND, node); |
| |
| case BAND_ASSIGN: |
| return binaryExpression(Types.BITWISE_AND_EQUAL, node); |
| |
| case BOR: |
| return binaryExpression(Types.BITWISE_OR, node); |
| |
| case BOR_ASSIGN: |
| return binaryExpression(Types.BITWISE_OR_EQUAL, node); |
| |
| case BXOR: |
| return binaryExpression(Types.BITWISE_XOR, node); |
| |
| case BXOR_ASSIGN: |
| return binaryExpression(Types.BITWISE_XOR_EQUAL, node); |
| |
| |
| case PLUS: |
| return binaryExpression(Types.PLUS, node); |
| |
| case PLUS_ASSIGN: |
| return binaryExpression(Types.PLUS_EQUAL, node); |
| |
| |
| case MINUS: |
| return binaryExpression(Types.MINUS, node); |
| |
| case MINUS_ASSIGN: |
| return binaryExpression(Types.MINUS_EQUAL, node); |
| |
| |
| case STAR: |
| return binaryExpression(Types.MULTIPLY, node); |
| |
| case STAR_ASSIGN: |
| return binaryExpression(Types.MULTIPLY_EQUAL, node); |
| |
| |
| case STAR_STAR: |
| return binaryExpression(Types.POWER, node); |
| |
| case STAR_STAR_ASSIGN: |
| return binaryExpression(Types.POWER_EQUAL, node); |
| |
| |
| case DIV: |
| return binaryExpression(Types.DIVIDE, node); |
| |
| case DIV_ASSIGN: |
| return binaryExpression(Types.DIVIDE_EQUAL, node); |
| |
| |
| case MOD: |
| return binaryExpression(Types.MOD, node); |
| |
| case MOD_ASSIGN: |
| return binaryExpression(Types.MOD_EQUAL, node); |
| |
| case SL: |
| return binaryExpression(Types.LEFT_SHIFT, node); |
| |
| case SL_ASSIGN: |
| return binaryExpression(Types.LEFT_SHIFT_EQUAL, node); |
| |
| case SR: |
| return binaryExpression(Types.RIGHT_SHIFT, node); |
| |
| case SR_ASSIGN: |
| return binaryExpression(Types.RIGHT_SHIFT_EQUAL, node); |
| |
| case BSR: |
| return binaryExpression(Types.RIGHT_SHIFT_UNSIGNED, node); |
| |
| case BSR_ASSIGN: |
| return binaryExpression(Types.RIGHT_SHIFT_UNSIGNED_EQUAL, node); |
| |
| case VARIABLE_DEF: |
| return declarationExpression(node); |
| |
| // Regex |
| case REGEX_FIND: |
| return binaryExpression(Types.FIND_REGEX, node); |
| |
| case REGEX_MATCH: |
| return binaryExpression(Types.MATCH_REGEX, node); |
| |
| |
| // Ranges |
| case RANGE_INCLUSIVE: |
| return rangeExpression(node, true); |
| |
| case RANGE_EXCLUSIVE: |
| return rangeExpression(node, false); |
| |
| case DYNAMIC_MEMBER: |
| return dynamicMemberExpression(node); |
| |
| case LITERAL_in: |
| return binaryExpression(Types.KEYWORD_IN, node); |
| |
| case ANNOTATION: |
| return new AnnotationConstantExpression(annotation(node)); |
| |
| case CLOSURE_LIST: |
| return closureListExpression(node); |
| |
| case LBRACK: |
| case LPAREN: |
| return tupleExpression(node); |
| |
| case OBJBLOCK: |
| return anonymousInnerClassDef(node); |
| |
| default: |
| unknownAST(node); |
| } |
| return null; |
| } |
| |
| private TupleExpression tupleExpression(AST node) { |
| TupleExpression exp = new TupleExpression(); |
| configureAST(exp, node); |
| node = node.getFirstChild(); |
| while (node != null) { |
| assertNodeType(VARIABLE_DEF, node); |
| AST nameNode = node.getFirstChild().getNextSibling(); |
| VariableExpression varExp = new VariableExpression(nameNode.getText()); |
| configureAST(varExp, nameNode); |
| exp.addExpression(varExp); |
| node = node.getNextSibling(); |
| } |
| return exp; |
| } |
| |
| private ClosureListExpression closureListExpression(AST node) { |
| isClosureListExpressionAllowedHere(node); |
| AST exprNode = node.getFirstChild(); |
| List<Expression> list = new LinkedList<Expression>(); |
| while (exprNode != null) { |
| if (isType(EXPR, exprNode)) { |
| Expression expr = expression(exprNode); |
| configureAST(expr, exprNode); |
| list.add(expr); |
| } else { |
| assertNodeType(EMPTY_STAT, exprNode); |
| list.add(EmptyExpression.INSTANCE); |
| } |
| |
| exprNode = exprNode.getNextSibling(); |
| } |
| ClosureListExpression cle = new ClosureListExpression(list); |
| configureAST(cle, node); |
| return cle; |
| } |
| |
| private void isClosureListExpressionAllowedHere(AST node) { |
| if (!forStatementBeingDef) { |
| throw new ASTRuntimeException(node, |
| "Expression list of the form (a; b; c) is not supported in this context."); |
| } |
| } |
| |
| protected Expression dynamicMemberExpression(AST dynamicMemberNode) { |
| AST node = dynamicMemberNode.getFirstChild(); |
| return expression(node); |
| } |
| |
| protected Expression ternaryExpression(AST ternaryNode) { |
| AST node = ternaryNode.getFirstChild(); |
| Expression base = expression(node); |
| node = node.getNextSibling(); |
| Expression left = expression(node); |
| node = node.getNextSibling(); |
| Expression ret; |
| if (node == null) { |
| ret = new ElvisOperatorExpression(base, left); |
| } else { |
| Expression right = expression(node); |
| BooleanExpression booleanExpression = new BooleanExpression(base); |
| booleanExpression.setSourcePosition(base); |
| ret = new TernaryExpression(booleanExpression, left, right); |
| } |
| configureAST(ret, ternaryNode); |
| return ret; |
| } |
| |
| protected Expression variableExpression(AST node) { |
| String text = node.getText(); |
| |
| // TODO we might wanna only try to resolve the name if we are |
| // on the left hand side of an expression or before a dot? |
| VariableExpression variableExpression = new VariableExpression(text); |
| configureAST(variableExpression, node); |
| return variableExpression; |
| } |
| |
| protected Expression literalExpression(AST node, Object value) { |
| ConstantExpression constantExpression = new ConstantExpression(value, value instanceof Boolean); |
| configureAST(constantExpression, node); |
| return constantExpression; |
| } |
| |
| protected Expression rangeExpression(AST rangeNode, boolean inclusive) { |
| AST node = rangeNode.getFirstChild(); |
| Expression left = expression(node); |
| Expression right = expression(node.getNextSibling()); |
| RangeExpression rangeExpression = new RangeExpression(left, right, inclusive); |
| configureAST(rangeExpression, rangeNode); |
| return rangeExpression; |
| } |
| |
| protected Expression spreadExpression(AST node) { |
| AST exprNode = node.getFirstChild(); |
| AST listNode = exprNode.getFirstChild(); |
| Expression right = expression(listNode); |
| SpreadExpression spreadExpression = new SpreadExpression(right); |
| configureAST(spreadExpression, node); |
| return spreadExpression; |
| } |
| |
| protected Expression spreadMapExpression(AST node) { |
| AST exprNode = node.getFirstChild(); |
| Expression expr = expression(exprNode); |
| SpreadMapExpression spreadMapExpression = new SpreadMapExpression(expr); |
| configureAST(spreadMapExpression, node); |
| return spreadMapExpression; |
| } |
| |
| protected Expression methodPointerExpression(AST node) { |
| AST exprNode = node.getFirstChild(); |
| Expression objectExpression = expression(exprNode); |
| AST mNode = exprNode.getNextSibling(); |
| Expression methodName; |
| if (isType(DYNAMIC_MEMBER, mNode)) { |
| methodName = expression(mNode); |
| } else { |
| methodName = new ConstantExpression(identifier(mNode)); |
| } |
| configureAST(methodName, mNode); |
| MethodPointerExpression methodPointerExpression = new MethodPointerExpression(objectExpression, methodName); |
| configureAST(methodPointerExpression, node); |
| return methodPointerExpression; |
| } |
| |
| /* commented out due to groovy.g non-determinisms |
| protected Expression defaultMethodPointerExpression(AST node) { |
| AST exprNode = node.getFirstChild(); |
| String methodName = exprNode.toString(); |
| MethodPointerExpression methodPointerExpression = new MethodPointerExpression(null, methodName); |
| configureAST(methodPointerExpression, node); |
| return methodPointerExpression; |
| } |
| */ |
| |
| protected Expression listExpression(AST listNode) { |
| List<Expression> expressions = new ArrayList<Expression>(); |
| AST elist = listNode.getFirstChild(); |
| assertNodeType(ELIST, elist); |
| |
| for (AST node = elist.getFirstChild(); node != null; node = node.getNextSibling()) { |
| // check for stray labeled arguments: |
| switch (node.getType()) { |
| case LABELED_ARG: |
| assertNodeType(COMMA, node); |
| break; // helpful error? |
| case SPREAD_MAP_ARG: |
| assertNodeType(SPREAD_ARG, node); |
| break; // helpful error |
| } |
| expressions.add(expression(node)); |
| } |
| ListExpression listExpression = new ListExpression(expressions); |
| configureAST(listExpression, listNode); |
| return listExpression; |
| } |
| |
| /** |
| * Typically only used for map constructors I think? |
| */ |
| protected Expression mapExpression(AST mapNode) { |
| List expressions = new ArrayList(); |
| AST elist = mapNode.getFirstChild(); |
| if (elist != null) { // totally empty in the case of [:] |
| assertNodeType(ELIST, elist); |
| for (AST node = elist.getFirstChild(); node != null; node = node.getNextSibling()) { |
| switch (node.getType()) { |
| case LABELED_ARG: |
| case SPREAD_MAP_ARG: |
| break; // legal cases |
| case SPREAD_ARG: |
| assertNodeType(SPREAD_MAP_ARG, node); |
| break; // helpful error |
| default: |
| assertNodeType(LABELED_ARG, node); |
| break; // helpful error |
| } |
| expressions.add(mapEntryExpression(node)); |
| } |
| } |
| MapExpression mapExpression = new MapExpression(expressions); |
| configureAST(mapExpression, mapNode); |
| return mapExpression; |
| } |
| |
| protected MapEntryExpression mapEntryExpression(AST node) { |
| if (node.getType() == SPREAD_MAP_ARG) { |
| AST rightNode = node.getFirstChild(); |
| Expression keyExpression = spreadMapExpression(node); |
| Expression rightExpression = expression(rightNode); |
| MapEntryExpression mapEntryExpression = new MapEntryExpression(keyExpression, rightExpression); |
| configureAST(mapEntryExpression, node); |
| return mapEntryExpression; |
| } else { |
| AST keyNode = node.getFirstChild(); |
| Expression keyExpression = expression(keyNode); |
| AST valueNode = keyNode.getNextSibling(); |
| Expression valueExpression = expression(valueNode); |
| MapEntryExpression mapEntryExpression = new MapEntryExpression(keyExpression, valueExpression); |
| configureAST(mapEntryExpression, node); |
| return mapEntryExpression; |
| } |
| } |
| |
| |
| protected Expression instanceofExpression(AST node) { |
| AST leftNode = node.getFirstChild(); |
| Expression leftExpression = expression(leftNode); |
| |
| AST rightNode = leftNode.getNextSibling(); |
| ClassNode type = buildName(rightNode); |
| assertTypeNotNull(type, rightNode); |
| |
| Expression rightExpression = new ClassExpression(type); |
| configureAST(rightExpression, rightNode); |
| BinaryExpression binaryExpression = new BinaryExpression(leftExpression, makeToken(Types.KEYWORD_INSTANCEOF, node), rightExpression); |
| configureAST(binaryExpression, node); |
| return binaryExpression; |
| } |
| |
| protected void assertTypeNotNull(ClassNode type, AST rightNode) { |
| if (type == null) { |
| throw new ASTRuntimeException(rightNode, "No type available for: " + qualifiedName(rightNode)); |
| } |
| } |
| |
| protected Expression asExpression(AST node) { |
| AST leftNode = node.getFirstChild(); |
| Expression leftExpression = expression(leftNode); |
| |
| AST rightNode = leftNode.getNextSibling(); |
| ClassNode type = makeTypeWithArguments(rightNode); |
| |
| return CastExpression.asExpression(type, leftExpression); |
| } |
| |
| protected Expression castExpression(AST castNode) { |
| AST node = castNode.getFirstChild(); |
| ClassNode type = makeTypeWithArguments(node); |
| assertTypeNotNull(type, node); |
| |
| AST expressionNode = node.getNextSibling(); |
| Expression expression = expression(expressionNode); |
| |
| CastExpression castExpression = new CastExpression(type, expression); |
| configureAST(castExpression, castNode); |
| return castExpression; |
| } |
| |
| |
| protected Expression indexExpression(AST indexNode) { |
| AST bracket = indexNode.getFirstChild(); |
| AST leftNode = bracket.getNextSibling(); |
| Expression leftExpression = expression(leftNode); |
| |
| AST rightNode = leftNode.getNextSibling(); |
| Expression rightExpression = expression(rightNode); |
| // easier to massage here than in the grammar |
| if (rightExpression instanceof SpreadExpression) { |
| ListExpression wrapped = new ListExpression(); |
| wrapped.addExpression(rightExpression); |
| rightExpression = wrapped; |
| } |
| |
| BinaryExpression binaryExpression = new BinaryExpression(leftExpression, makeToken(Types.LEFT_SQUARE_BRACKET, bracket), rightExpression); |
| configureAST(binaryExpression, indexNode); |
| return binaryExpression; |
| } |
| |
| protected Expression binaryExpression(int type, AST node) { |
| Token token = makeToken(type, node); |
| |
| AST leftNode = node.getFirstChild(); |
| Expression leftExpression = expression(leftNode); |
| |
| AST rightNode = leftNode.getNextSibling(); |
| if (rightNode == null) { |
| return leftExpression; |
| } |
| |
| if (Types.ofType(type, Types.ASSIGNMENT_OPERATOR)) { |
| if (leftExpression instanceof VariableExpression || |
| leftExpression.getClass() == PropertyExpression.class || |
| leftExpression instanceof FieldExpression || |
| leftExpression instanceof AttributeExpression || |
| leftExpression instanceof DeclarationExpression || |
| leftExpression instanceof TupleExpression) { |
| // Do nothing. |
| } else if (leftExpression instanceof ConstantExpression) { |
| throw new ASTRuntimeException(node, "\n[" + ((ConstantExpression) leftExpression).getValue() + "] is a constant expression, but it should be a variable expression"); |
| } else if (leftExpression instanceof BinaryExpression) { |
| int lefttype = ((BinaryExpression) leftExpression).getOperation().getType(); |
| if (!Types.ofType(lefttype, Types.ASSIGNMENT_OPERATOR) && lefttype != Types.LEFT_SQUARE_BRACKET) { |
| throw new ASTRuntimeException(node, "\n" + ((BinaryExpression) leftExpression).getText() + " is a binary expression, but it should be a variable expression"); |
| } |
| } else if (leftExpression instanceof GStringExpression) { |
| throw new ASTRuntimeException(node, "\n\"" + ((GStringExpression) leftExpression).getText() + "\" is a GString expression, but it should be a variable expression"); |
| } else if (leftExpression instanceof MethodCallExpression) { |
| throw new ASTRuntimeException(node, "\n\"" + ((MethodCallExpression) leftExpression).getText() + "\" is a method call expression, but it should be a variable expression"); |
| } else if (leftExpression instanceof MapExpression) { |
| throw new ASTRuntimeException(node, "\n'" + ((MapExpression) leftExpression).getText() + "' is a map expression, but it should be a variable expression"); |
| } else { |
| throw new ASTRuntimeException(node, "\n" + leftExpression.getClass() + ", with its value '" + leftExpression.getText() + "', is a bad expression as the left hand side of an assignment operator"); |
| } |
| } |
| /*if (rightNode == null) { |
| throw new NullPointerException("No rightNode associated with binary expression"); |
| }*/ |
| Expression rightExpression = expression(rightNode); |
| BinaryExpression binaryExpression = new BinaryExpression(leftExpression, token, rightExpression); |
| configureAST(binaryExpression, node); |
| return binaryExpression; |
| } |
| |
| protected Expression prefixExpression(AST node, int token) { |
| Expression expression = expression(node.getFirstChild()); |
| PrefixExpression prefixExpression = new PrefixExpression(makeToken(token, node), expression); |
| configureAST(prefixExpression, node); |
| return prefixExpression; |
| } |
| |
| protected Expression postfixExpression(AST node, int token) { |
| Expression expression = expression(node.getFirstChild()); |
| PostfixExpression postfixExpression = new PostfixExpression(expression, makeToken(token, node)); |
| configureAST(postfixExpression, node); |
| return postfixExpression; |
| } |
| |
| protected BooleanExpression booleanExpression(AST node) { |
| BooleanExpression booleanExpression = new BooleanExpression(expression(node)); |
| configureAST(booleanExpression, node); |
| return booleanExpression; |
| } |
| |
| protected Expression dotExpression(AST node) { |
| // let's decide if this is a property invocation or a method call |
| AST leftNode = node.getFirstChild(); |
| if (leftNode != null) { |
| AST identifierNode = leftNode.getNextSibling(); |
| if (identifierNode != null) { |
| Expression leftExpression = expression(leftNode); |
| if (isType(SELECT_SLOT, identifierNode)) { |
| Expression field = expression(identifierNode.getFirstChild(), true); |
| AttributeExpression attributeExpression = new AttributeExpression(leftExpression, field, node.getType() != DOT); |
| if (node.getType() == SPREAD_DOT) { |
| attributeExpression.setSpreadSafe(true); |
| } |
| configureAST(attributeExpression, node); |
| return attributeExpression; |
| } |
| if (isType(SLIST, identifierNode)) { |
| Statement code = statementList(identifierNode); |
| ClosureExpression closureExpression = new ClosureExpression(Parameter.EMPTY_ARRAY, code); |
| configureAST(closureExpression, identifierNode); |
| final PropertyExpression propertyExpression = new PropertyExpression(leftExpression, closureExpression); |
| if (node.getType() == SPREAD_DOT) { |
| propertyExpression.setSpreadSafe(true); |
| } |
| configureAST(propertyExpression, node); |
| return propertyExpression; |
| } |
| Expression property = expression(identifierNode, true); |
| |
| |
| // A."this" assumes a VariableExpression can be used for "this" |
| // we correct that here into a ConstantExpression |
| if (property instanceof VariableExpression) { |
| VariableExpression ve = (VariableExpression) property; |
| property = new ConstantExpression(ve.getName()); |
| } |
| |
| PropertyExpression propertyExpression = new PropertyExpression(leftExpression, property, node.getType() != DOT); |
| if (node.getType() == SPREAD_DOT) { |
| propertyExpression.setSpreadSafe(true); |
| } |
| configureAST(propertyExpression, node); |
| return propertyExpression; |
| } |
| } |
| return methodCallExpression(node); |
| } |
| |
| protected Expression specialConstructorCallExpression(AST methodCallNode, ClassNode special) { |
| AST node = methodCallNode.getFirstChild(); |
| Expression arguments = arguments(node); |
| |
| ConstructorCallExpression expression = new ConstructorCallExpression(special, arguments); |
| configureAST(expression, methodCallNode); |
| return expression; |
| } |
| |
| protected Expression methodCallExpression(AST methodCallNode) { |
| AST node = methodCallNode.getFirstChild(); |
| Expression objectExpression; |
| AST selector; |
| AST elist = node.getNextSibling(); |
| List<GenericsType> typeArgumentList = null; |
| |
| boolean implicitThis = false; |
| boolean safe = isType(OPTIONAL_DOT, node); |
| boolean spreadSafe = isType(SPREAD_DOT, node); |
| if (isType(DOT, node) || safe || spreadSafe) { |
| AST objectNode = node.getFirstChild(); |
| objectExpression = expression(objectNode); |
| selector = objectNode.getNextSibling(); |
| } else { |
| implicitThis = true; |
| objectExpression = VariableExpression.THIS_EXPRESSION; |
| selector = node; |
| } |
| |
| if (isType(TYPE_ARGUMENTS, selector)) { |
| typeArgumentList = getTypeArgumentsList(selector); |
| selector = selector.getNextSibling(); |
| } |
| |
| Expression name = null; |
| if (isType(LITERAL_super, selector)) { |
| implicitThis = true; |
| name = new ConstantExpression("super"); |
| if (objectExpression instanceof VariableExpression && ((VariableExpression) objectExpression).isThisExpression()) { |
| objectExpression = VariableExpression.SUPER_EXPRESSION; |
| } |
| } else if (isPrimitiveTypeLiteral(selector)) { |
| throw new ASTRuntimeException(selector, "Primitive type literal: " + selector.getText() |
| + " cannot be used as a method name"); |
| } else if (isType(SELECT_SLOT, selector)) { |
| Expression field = expression(selector.getFirstChild(), true); |
| AttributeExpression attributeExpression = new AttributeExpression(objectExpression, field, node.getType() != DOT); |
| configureAST(attributeExpression, node); |
| Expression arguments = arguments(elist); |
| MethodCallExpression expression = new MethodCallExpression(attributeExpression, "call", arguments); |
| setTypeArgumentsOnMethodCallExpression(expression, typeArgumentList); |
| configureAST(expression, methodCallNode); |
| return expression; |
| } else if (!implicitThis || isType(DYNAMIC_MEMBER, selector) || isType(IDENT, selector) || |
| isType(STRING_CONSTRUCTOR, selector) || isType(STRING_LITERAL, selector)) { |
| name = expression(selector, true); |
| } else { |
| implicitThis = false; |
| name = new ConstantExpression("call"); |
| objectExpression = expression(selector, true); |
| } |
| |
| // if node text is found to be "super"/"this" when a method call is being processed, it is a |
| // call like this(..)/super(..) after the first statement, which shouldn't be allowed. GROOVY-2836 |
| if (selector.getText().equals("this") || selector.getText().equals("super")) { |
| if (!(annotationBeingDef && selector.getText().equals("super"))) { |
| throw new ASTRuntimeException(elist, "Constructor call must be the first statement in a constructor."); |
| } |
| } |
| |
| Expression arguments = arguments(elist); |
| MethodCallExpression expression = new MethodCallExpression(objectExpression, name, arguments); |
| expression.setSafe(safe); |
| expression.setSpreadSafe(spreadSafe); |
| expression.setImplicitThis(implicitThis); |
| setTypeArgumentsOnMethodCallExpression(expression, typeArgumentList); |
| Expression ret = expression; |
| //FIXME: do we really want this() to create a new object regardless |
| // the position.. for example not as first statement in a constructor |
| // this=first statement in constructor is handled by specialConstructorCallExpression |
| // we may have to add a check and remove this part of the code |
| if (implicitThis && "this".equals(expression.getMethodAsString())) { |
| ret = new ConstructorCallExpression(this.classNode, arguments); |
| } |
| configureAST(ret, methodCallNode); |
| return ret; |
| } |
| |
| private static void setTypeArgumentsOnMethodCallExpression(MethodCallExpression expression, |
| List<GenericsType> typeArgumentList) { |
| if (typeArgumentList != null && !typeArgumentList.isEmpty()) { |
| expression.setGenericsTypes(typeArgumentList.toArray(new GenericsType[typeArgumentList.size()])); |
| } |
| } |
| |
| protected Expression constructorCallExpression(AST node) { |
| AST constructorCallNode = node; |
| ClassNode type = makeTypeWithArguments(constructorCallNode); |
| |
| if (isType(CTOR_CALL, node) || isType(LITERAL_new, node)) { |
| node = node.getFirstChild(); |
| } |
| |
| AST elist = node.getNextSibling(); |
| |
| if (elist == null && isType(ELIST, node)) { |
| elist = node; |
| if ("(".equals(type.getName())) { |
| type = classNode; |
| } |
| } |
| |
| if (isType(ARRAY_DECLARATOR, elist)) { |
| AST expressionNode = elist.getFirstChild(); |
| if (expressionNode == null) { |
| throw new ASTRuntimeException(elist, "No expression for the array constructor call"); |
| } |
| List size = arraySizeExpression(expressionNode); |
| ArrayExpression arrayExpression = new ArrayExpression(type, null, size); |
| configureAST(arrayExpression, constructorCallNode); |
| return arrayExpression; |
| } |
| Expression arguments = arguments(elist); |
| ClassNode innerClass = getAnonymousInnerClassNode(arguments); |
| ConstructorCallExpression ret = new ConstructorCallExpression(type, arguments); |
| if (innerClass != null) { |
| ret.setType(innerClass); |
| ret.setUsingAnonymousInnerClass(true); |
| innerClass.setUnresolvedSuperClass(type); |
| } |
| |
| configureAST(ret, constructorCallNode); |
| return ret; |
| } |
| |
| private static ClassNode getAnonymousInnerClassNode(Expression arguments) { |
| if (arguments instanceof TupleExpression) { |
| TupleExpression te = (TupleExpression) arguments; |
| List<Expression> expressions = te.getExpressions(); |
| if (expressions.isEmpty()) return null; |
| Expression last = expressions.remove(expressions.size() - 1); |
| if (last instanceof AnonymousInnerClassCarrier) { |
| AnonymousInnerClassCarrier carrier = (AnonymousInnerClassCarrier) last; |
| return carrier.innerClass; |
| } else { |
| expressions.add(last); |
| } |
| } else if (arguments instanceof AnonymousInnerClassCarrier) { |
| AnonymousInnerClassCarrier carrier = (AnonymousInnerClassCarrier) arguments; |
| return carrier.innerClass; |
| } |
| return null; |
| } |
| |
| protected List arraySizeExpression(AST node) { |
| List list; |
| Expression size = null; |
| if (isType(ARRAY_DECLARATOR, node)) { |
| AST right = node.getNextSibling(); |
| if (right != null) { |
| size = expression(right); |
| } else { |
| size = ConstantExpression.EMPTY_EXPRESSION; |
| } |
| AST child = node.getFirstChild(); |
| if (child == null) { |
| throw new ASTRuntimeException(node, "No expression for the array constructor call"); |
| } |
| list = arraySizeExpression(child); |
| } else { |
| size = expression(node); |
| list = new ArrayList(); |
| } |
| list.add(size); |
| return list; |
| } |
| |
| protected Expression enumArguments(AST elist) { |
| List<Expression> expressionList = new ArrayList<Expression>(); |
| for (AST node = elist; node != null; node = node.getNextSibling()) { |
| Expression expression = expression(node); |
| expressionList.add(expression); |
| } |
| ArgumentListExpression argumentListExpression = new ArgumentListExpression(expressionList); |
| configureAST(argumentListExpression, elist); |
| return argumentListExpression; |
| } |
| |
| protected Expression arguments(AST elist) { |
| List expressionList = new ArrayList(); |
| // FIXME: all labeled arguments should follow any unlabeled arguments |
| boolean namedArguments = false; |
| for (AST node = elist; node != null; node = node.getNextSibling()) { |
| if (isType(ELIST, node)) { |
| for (AST child = node.getFirstChild(); child != null; child = child.getNextSibling()) { |
| namedArguments |= addArgumentExpression(child, expressionList); |
| } |
| } else { |
| namedArguments |= addArgumentExpression(node, expressionList); |
| } |
| } |
| if (namedArguments) { |
| if (!expressionList.isEmpty()) { |
| // let's remove any non-MapEntryExpression instances |
| // such as if the last expression is a ClosureExpression |
| // so let's wrap the named method calls in a Map expression |
| List<Expression> argumentList = new ArrayList<Expression>(); |
| for (Object next : expressionList) { |
| Expression expression = (Expression) next; |
| if (!(expression instanceof MapEntryExpression)) { |
| argumentList.add(expression); |
| } |
| } |
| if (!argumentList.isEmpty()) { |
| expressionList.removeAll(argumentList); |
| checkDuplicateNamedParams(elist, expressionList); |
| MapExpression mapExpression = new MapExpression(expressionList); |
| configureAST(mapExpression, elist); |
| argumentList.add(0, mapExpression); |
| ArgumentListExpression argumentListExpression = new ArgumentListExpression(argumentList); |
| configureAST(argumentListExpression, elist); |
| return argumentListExpression; |
| } |
| } |
| checkDuplicateNamedParams(elist, expressionList); |
| NamedArgumentListExpression namedArgumentListExpression = new NamedArgumentListExpression(expressionList); |
| configureAST(namedArgumentListExpression, elist); |
| return namedArgumentListExpression; |
| } else { |
| ArgumentListExpression argumentListExpression = new ArgumentListExpression(expressionList); |
| configureAST(argumentListExpression, elist); |
| return argumentListExpression; |
| } |
| } |
| |
| private static void checkDuplicateNamedParams(AST elist, List expressionList) { |
| if (expressionList.isEmpty()) return; |
| |
| Set<String> namedArgumentNames = new HashSet<String>(); |
| for (Object expression : expressionList) { |
| MapEntryExpression meExp = (MapEntryExpression) expression; |
| if (meExp.getKeyExpression() instanceof ConstantExpression) { |
| String argName = meExp.getKeyExpression().getText(); |
| if (!namedArgumentNames.contains(argName)) { |
| namedArgumentNames.add(argName); |
| } else { |
| throw new ASTRuntimeException(elist, "Duplicate named parameter '" + argName |
| + "' found."); |
| } |
| } |
| } |
| } |
| |
| protected boolean addArgumentExpression(AST node, List<Expression> expressionList) { |
| if (node.getType() == SPREAD_MAP_ARG) { |
| AST rightNode = node.getFirstChild(); |
| Expression keyExpression = spreadMapExpression(node); |
| Expression rightExpression = expression(rightNode); |
| MapEntryExpression mapEntryExpression = new MapEntryExpression(keyExpression, rightExpression); |
| expressionList.add(mapEntryExpression); |
| return true; |
| } else { |
| Expression expression = expression(node); |
| expressionList.add(expression); |
| return expression instanceof MapEntryExpression; |
| } |
| } |
| |
| protected Expression expressionList(AST node) { |
| List<Expression> expressionList = new ArrayList<Expression>(); |
| for (AST child = node.getFirstChild(); child != null; child = child.getNextSibling()) { |
| expressionList.add(expression(child)); |
| } |
| if (expressionList.size() == 1) { |
| return expressionList.get(0); |
| } else { |
| ListExpression listExpression = new ListExpression(expressionList); |
| listExpression.setWrapped(true); |
| configureAST(listExpression, node); |
| return listExpression; |
| } |
| } |
| |
| protected ClosureExpression closureExpression(AST node) { |
| AST paramNode = node.getFirstChild(); |
| Parameter[] parameters = null; |
| AST codeNode = paramNode; |
| if (isType(PARAMETERS, paramNode) || isType(IMPLICIT_PARAMETERS, paramNode)) { |
| parameters = parameters(paramNode); |
| codeNode = paramNode.getNextSibling(); |
| } |
| Statement code = statementListNoChild(codeNode, node); |
| ClosureExpression closureExpression = new ClosureExpression(parameters, code); |
| configureAST(closureExpression, node); |
| return closureExpression; |
| } |
| |
| protected Expression blockExpression(AST node) { |
| AST codeNode = node.getFirstChild(); |
| if (codeNode == null) return ConstantExpression.NULL; |
| if (codeNode.getType() == EXPR && codeNode.getNextSibling() == null) { |
| // Simplify common case of {expr} to expr. |
| return expression(codeNode); |
| } |
| Parameter[] parameters = Parameter.EMPTY_ARRAY; |
| Statement code = statementListNoChild(codeNode, node); |
| ClosureExpression closureExpression = new ClosureExpression(parameters, code); |
| configureAST(closureExpression, node); |
| // Call it immediately. |
| String callName = "call"; |
| Expression noArguments = new ArgumentListExpression(); |
| MethodCallExpression call = new MethodCallExpression(closureExpression, callName, noArguments); |
| configureAST(call, node); |
| return call; |
| } |
| |
| protected Expression unaryMinusExpression(AST unaryMinusExpr) { |
| AST node = unaryMinusExpr.getFirstChild(); |
| |
| // if we are a number literal then let's just parse it |
| // as the negation operator on MIN_INT causes rounding to a long |
| String text = node.getText(); |
| switch (node.getType()) { |
| case NUM_DOUBLE: |
| case NUM_FLOAT: |
| case NUM_BIG_DECIMAL: |
| ConstantExpression constantExpression = new ConstantExpression(Numbers.parseDecimal("-" + text)); |
| configureAST(constantExpression, unaryMinusExpr); |
| return constantExpression; |
| |
| case NUM_BIG_INT: |
| case NUM_INT: |
| case NUM_LONG: |
| ConstantExpression constantLongExpression = new ConstantExpression(Numbers.parseInteger(unaryMinusExpr,"-" + text)); |
| configureAST(constantLongExpression, unaryMinusExpr); |
| return constantLongExpression; |
| |
| default: |
| UnaryMinusExpression unaryMinusExpression = new UnaryMinusExpression(expression(node)); |
| configureAST(unaryMinusExpression, unaryMinusExpr); |
| return unaryMinusExpression; |
| } |
| } |
| |
| protected Expression unaryPlusExpression(AST unaryPlusExpr) { |
| AST node = unaryPlusExpr.getFirstChild(); |
| switch (node.getType()) { |
| case NUM_DOUBLE: |
| case NUM_FLOAT: |
| case NUM_BIG_DECIMAL: |
| case NUM_BIG_INT: |
| case NUM_INT: |
| case NUM_LONG: |
| return expression(node); |
| |
| default: |
| UnaryPlusExpression unaryPlusExpression = new UnaryPlusExpression(expression(node)); |
| configureAST(unaryPlusExpression, unaryPlusExpr); |
| return unaryPlusExpression; |
| } |
| } |
| |
| protected ConstantExpression decimalExpression(AST node) { |
| String text = node.getText(); |
| Object number = Numbers.parseDecimal(text); |
| ConstantExpression constantExpression = new ConstantExpression(number, |
| number instanceof Double || number instanceof Float); |
| configureAST(constantExpression, node); |
| return constantExpression; |
| } |
| |
| protected ConstantExpression integerExpression(AST node) { |
| String text = node.getText(); |
| Object number = Numbers.parseInteger(node, text); |
| boolean keepPrimitive = number instanceof Integer || number instanceof Long; |
| ConstantExpression constantExpression = new ConstantExpression(number, keepPrimitive); |
| configureAST(constantExpression, node); |
| return constantExpression; |
| } |
| |
| protected Expression gstring(AST gstringNode) { |
| List strings = new ArrayList(); |
| List values = new ArrayList(); |
| |
| StringBuilder buffer = new StringBuilder(); |
| |
| boolean isPrevString = false; |
| |
| for (AST node = gstringNode.getFirstChild(); node != null; node = node.getNextSibling()) { |
| int type = node.getType(); |
| String text = null; |
| switch (type) { |
| |
| case STRING_LITERAL: |
| if (isPrevString) assertNodeType(IDENT, node); // parser bug |
| isPrevString = true; |
| text = node.getText(); |
| ConstantExpression constantExpression = new ConstantExpression(text); |
| configureAST(constantExpression, node); |
| strings.add(constantExpression); |
| buffer.append(text); |
| break; |
| |
| default: { |
| if (!isPrevString) assertNodeType(IDENT, node); // parser bug |
| isPrevString = false; |
| Expression expression = expression(node); |
| values.add(expression); |
| buffer.append("$"); |
| buffer.append(expression.getText()); |
| } |
| break; |
| } |
| } |
| GStringExpression gStringExpression = new GStringExpression(buffer.toString(), strings, values); |
| configureAST(gStringExpression, gstringNode); |
| return gStringExpression; |
| } |
| |
| protected ClassNode type(AST typeNode) { |
| // TODO intern types? |
| // TODO configureAST(...) |
| return buildName(typeNode.getFirstChild()); |
| } |
| |
| public static String qualifiedName(AST qualifiedNameNode) { |
| if (isType(IDENT, qualifiedNameNode)) { |
| return qualifiedNameNode.getText(); |
| } |
| if (isType(DOT, qualifiedNameNode)) { |
| AST node = qualifiedNameNode.getFirstChild(); |
| StringBuilder buffer = new StringBuilder(); |
| boolean first = true; |
| |
| for (; node != null && !isType(TYPE_ARGUMENTS, node); node = node.getNextSibling()) { |
| if (first) { |
| first = false; |
| } else { |
| buffer.append("."); |
| } |
| buffer.append(qualifiedName(node)); |
| } |
| return buffer.toString(); |
| } else { |
| return qualifiedNameNode.getText(); |
| } |
| } |
| |
| private int getBoundType(AST node) { |
| if (node == null) return -1; |
| if (isType(TYPE_UPPER_BOUNDS, node)) return TYPE_UPPER_BOUNDS; |
| if (isType(TYPE_LOWER_BOUNDS, node)) return TYPE_LOWER_BOUNDS; |
| throw new ASTRuntimeException(node, |
| "Unexpected node type: " + getTokenName(node) + |
| " found when expecting type: " + getTokenName(TYPE_UPPER_BOUNDS) + |
| " or type: " + getTokenName(TYPE_LOWER_BOUNDS)); |
| } |
| |
| private GenericsType makeGenericsArgumentType(AST typeArgument) { |
| GenericsType gt; |
| AST rootNode = typeArgument.getFirstChild(); |
| if (isType(WILDCARD_TYPE, rootNode)) { |
| ClassNode base = ClassHelper.makeWithoutCaching("?"); |
| if (rootNode.getNextSibling() != null) { |
| int boundType = getBoundType(rootNode.getNextSibling()); |
| ClassNode[] gts = makeGenericsBounds(rootNode, boundType); |
| if (boundType == TYPE_UPPER_BOUNDS) { |
| gt = new GenericsType(base, gts, null); |
| } else { |
| gt = new GenericsType(base, null, gts[0]); |
| } |
| } else { |
| gt = new GenericsType(base, null, null); |
| } |
| gt.setName("?"); |
| gt.setWildcard(true); |
| } else { |
| ClassNode argument = makeTypeWithArguments(rootNode); |
| gt = new GenericsType(argument); |
| } |
| configureAST(gt, typeArgument); |
| return gt; |
| } |
| |
| protected ClassNode makeTypeWithArguments(AST rootNode) { |
| ClassNode basicType = makeType(rootNode); |
| AST node = rootNode.getFirstChild(); |
| if (node == null || isType(INDEX_OP, node) || isType(ARRAY_DECLARATOR, node)) return basicType; |
| |
| if (!isType(DOT, node)) { |
| node = node.getFirstChild(); |
| if (node == null) return basicType; |
| return addTypeArguments(basicType, node); |
| } else { |
| node = node.getFirstChild(); |
| while (node != null && !isType(TYPE_ARGUMENTS, node)) |
| node = node.getNextSibling(); |
| return node == null ? basicType : addTypeArguments(basicType, node); |
| } |
| } |
| |
| private ClassNode addTypeArguments(ClassNode basicType, AST node) { |
| List<GenericsType> typeArgumentList = getTypeArgumentsList(node); |
| // a 0-length type argument list means we face the diamond operator |
| basicType.setGenericsTypes(typeArgumentList.toArray(new GenericsType[typeArgumentList.size()])); |
| return basicType; |
| } |
| |
| private List<GenericsType> getTypeArgumentsList(AST node) { |
| assertNodeType(TYPE_ARGUMENTS, node); |
| List<GenericsType> typeArgumentList = new LinkedList<GenericsType>(); |
| AST typeArgument = node.getFirstChild(); |
| |
| while (typeArgument != null) { |
| assertNodeType(TYPE_ARGUMENT, typeArgument); |
| GenericsType gt = makeGenericsArgumentType(typeArgument); |
| typeArgumentList.add(gt); |
| typeArgument = typeArgument.getNextSibling(); |
| } |
| return typeArgumentList; |
| } |
| |
| private ClassNode[] makeGenericsBounds(AST rn, int boundType) { |
| AST boundsRoot = rn.getNextSibling(); |
| if (boundsRoot == null) return null; |
| assertNodeType(boundType, boundsRoot); |
| LinkedList bounds = new LinkedList(); |
| for (AST boundsNode = boundsRoot.getFirstChild(); |
| boundsNode != null; |
| boundsNode = boundsNode.getNextSibling() |
| ) { |
| ClassNode bound = null; |
| bound = makeTypeWithArguments(boundsNode); |
| configureAST(bound, boundsNode); |
| bounds.add(bound); |
| } |
| if (bounds.isEmpty()) return null; |
| return (ClassNode[]) bounds.toArray(new ClassNode[bounds.size()]); |
| } |
| |
| protected GenericsType[] makeGenericsType(AST rootNode) { |
| AST typeParameter = rootNode.getFirstChild(); |
| LinkedList ret = new LinkedList(); |
| assertNodeType(TYPE_PARAMETER, typeParameter); |
| |
| while (isType(TYPE_PARAMETER, typeParameter)) { |
| AST typeNode = typeParameter.getFirstChild(); |
| ClassNode type = makeType(typeParameter); |
| |
| GenericsType gt = new GenericsType(type, makeGenericsBounds(typeNode, TYPE_UPPER_BOUNDS), null); |
| configureAST(gt, typeParameter); |
| |
| ret.add(gt); |
| typeParameter = typeParameter.getNextSibling(); |
| } |
| return (GenericsType[]) ret.toArray(new GenericsType[ret.size()]); |
| } |
| |
| protected ClassNode makeType(AST typeNode) { |
| ClassNode answer = ClassHelper.DYNAMIC_TYPE; |
| AST node = typeNode.getFirstChild(); |
| if (node != null) { |
| if (isType(INDEX_OP, node) || isType(ARRAY_DECLARATOR, node)) { |
| answer = makeType(node).makeArray(); |
| } else { |
| answer = ClassHelper.make(qualifiedName(node)); |
| if (answer.isUsingGenerics()) { |
| ClassNode newAnswer = ClassHelper.makeWithoutCaching(answer.getName()); |
| newAnswer.setRedirect(answer); |
| answer = newAnswer; |
| } |
| } |
| configureAST(answer, node); |
| } |
| return answer; |
| } |
| |
| /** |
| * Performs a name resolution to see if the given name is a type from imports, |
| * aliases or newly created classes |
| */ |
| /*protected String resolveTypeName(String name, boolean safe) { |
| if (name == null) { |
| return null; |
| } |
| return resolveNewClassOrName(name, safe); |
| }*/ |
| |
| /** |
| * Extracts an identifier from the Antlr AST and then performs a name resolution |
| * to see if the given name is a type from imports, aliases or newly created classes |
| */ |
| protected ClassNode buildName(AST node) { |
| if (isType(TYPE, node)) { |
| node = node.getFirstChild(); |
| } |
| ClassNode answer = null; |
| if (isType(DOT, node) || isType(OPTIONAL_DOT, node)) { |
| answer = ClassHelper.make(qualifiedName(node)); |
| } else if (isPrimitiveTypeLiteral(node)) { |
| answer = ClassHelper.make(node.getText()); |
| } else if (isType(INDEX_OP, node) || isType(ARRAY_DECLARATOR, node)) { |
| AST child = node.getFirstChild(); |
| answer = buildName(child).makeArray(); |
| configureAST(answer, node); |
| return answer; |
| } else { |
| String identifier = node.getText(); |
| answer = ClassHelper.make(identifier); |
| } |
| AST nextSibling = node.getNextSibling(); |
| if (isType(INDEX_OP, nextSibling) || isType(ARRAY_DECLARATOR, node)) { |
| answer = answer.makeArray(); |
| configureAST(answer, node); |
| return answer; |
| } else { |
| configureAST(answer, node); |
| return answer; |
| } |
| } |
| |
| protected boolean isPrimitiveTypeLiteral(AST node) { |
| int type = node.getType(); |
| switch (type) { |
| case LITERAL_boolean: |
| case LITERAL_byte: |
| case LITERAL_char: |
| case LITERAL_double: |
| case LITERAL_float: |
| case LITERAL_int: |
| case LITERAL_long: |
| case LITERAL_short: |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| /** |
| * Extracts an identifier from the Antlr AST |
| */ |
| protected String identifier(AST node) { |
| assertNodeType(IDENT, node); |
| return node.getText(); |
| } |
| |
| protected String label(AST labelNode) { |
| AST node = labelNode.getFirstChild(); |
| if (node == null) { |
| return null; |
| } |
| return identifier(node); |
| } |
| |
| |
| // Helper methods |
| //------------------------------------------------------------------------- |
| |
| |
| /** |
| * Returns true if the modifiers flags contain a visibility modifier |
| */ |
| protected boolean hasVisibility(int modifiers) { |
| return (modifiers & (Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC)) != 0; |
| } |
| |
| protected void configureAST(ASTNode node, AST ast) { |
| if (ast == null) |
| throw new ASTRuntimeException(ast, "PARSER BUG: Tried to configure " + node.getClass().getName() + " with null Node"); |
| node.setColumnNumber(ast.getColumn()); |
| node.setLineNumber(ast.getLine()); |
| if (ast instanceof GroovySourceAST) { |
| node.setLastColumnNumber(((GroovySourceAST) ast).getColumnLast()); |
| node.setLastLineNumber(((GroovySourceAST) ast).getLineLast()); |
| } |
| |
| // TODO we could one day store the Antlr AST on the Groovy AST |
| // node.setCSTNode(ast); |
| } |
| |
| protected static Token makeToken(int typeCode, AST node) { |
| return Token.newSymbol(typeCode, node.getLine(), node.getColumn()); |
| } |
| |
| protected String getFirstChildText(AST node) { |
| AST child = node.getFirstChild(); |
| return child != null ? child.getText() : null; |
| } |
| |
| |
| public static boolean isType(int typeCode, AST node) { |
| return node != null && node.getType() == typeCode; |
| } |
| |
| private String getTokenName(int token) { |
| if (tokenNames == null) return "" + token; |
| return tokenNames[token]; |
| } |
| |
| private String getTokenName(AST node) { |
| if (node == null) return "null"; |
| return getTokenName(node.getType()); |
| } |
| |
| protected void assertNodeType(int type, AST node) { |
| if (node == null) { |
| throw new ASTRuntimeException(node, "No child node available in AST when expecting type: " + getTokenName(type)); |
| } |
| if (node.getType() != type) { |
| throw new ASTRuntimeException(node, "Unexpected node type: " + getTokenName(node) + " found when expecting type: " + getTokenName(type)); |
| } |
| } |
| |
| protected void notImplementedYet(AST node) { |
| throw new ASTRuntimeException(node, "AST node not implemented yet for type: " + getTokenName(node)); |
| } |
| |
| protected void unknownAST(AST node) { |
| if (node.getType() == CLASS_DEF) { |
| throw new ASTRuntimeException(node, |
| "Class definition not expected here. Please define the class at an appropriate place or perhaps try using a block/Closure instead."); |
| } |
| if (node.getType() == METHOD_DEF) { |
| throw new ASTRuntimeException(node, |
| "Method definition not expected here. Please define the method at an appropriate place or perhaps try using a block/Closure instead."); |
| } |
| throw new ASTRuntimeException(node, "Unknown type: " + getTokenName(node)); |
| } |
| |
| protected void dumpTree(AST ast) { |
| for (AST node = ast.getFirstChild(); node != null; node = node.getNextSibling()) { |
| dump(node); |
| } |
| } |
| |
| protected void dump(AST node) { |
| System.out.println("Type: " + getTokenName(node) + " text: " + node.getText()); |
| } |
| } |