Revision: 5839 Author: [email protected] Date: Thu Jul 30 09:28:03 2009 Log: Checkpoint work to get real argument names from source.
http://code.google.com/p/google-web-toolkit/source/detail?r=5839 Added: /changes/jat/ihm/dev/core/src/com/google/gwt/dev/javac/JavaSourceParser.java /changes/jat/ihm/dev/core/test/com/google/gwt/dev/javac/JavaSourceParserTest.java Modified: /changes/jat/ihm/dev/core/src/com/google/gwt/core/ext/typeinfo/JAbstractMethod.java /changes/jat/ihm/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java /changes/jat/ihm/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameter.java /changes/jat/ihm/dev/core/src/com/google/gwt/dev/javac/TypeOracleMediator.java ======================================= --- /dev/null +++ /changes/jat/ihm/dev/core/src/com/google/gwt/dev/javac/JavaSourceParser.java Thu Jul 30 09:28:03 2009 @@ -0,0 +1,157 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed 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 com.google.gwt.dev.javac; + +import com.google.gwt.core.ext.typeinfo.JAbstractMethod; +import com.google.gwt.dev.util.Name.BinaryName; + +import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; +import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; +import org.eclipse.jdt.internal.core.util.CodeSnippetParsingUtil; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Methods to do direct parsing of Java source -- currently the only uses are + * for finding actual method parameter names on request. + */ +public class JavaSourceParser { + + /** + * Parse Java source. + * + * @param javaSource String containing Java source to parse + * @return a CompilationUnitDeclaration or null if parsing failed + */ + private static CompilationUnitDeclaration parseJava(String javaSource) { + CodeSnippetParsingUtil parsingUtil = new CodeSnippetParsingUtil(); + CompilerOptions options = new CompilerOptions(); + options.complianceLevel = ClassFileConstants.JDK1_5; + options.sourceLevel = ClassFileConstants.JDK1_5; + CompilationUnitDeclaration unit = parsingUtil.parseCompilationUnit( + javaSource.toString().toCharArray(), options.getMap(), true); + if (unit.compilationResult().hasProblems()) { + return null; + } + return unit; + } + + /** + * Spits a binary name into a series of char arrays, corresponding to + * enclosing classes. + * + * <p>For example, {...@code test.Foo$Bar} gets expanded to [[Foo],[Bar]]. + * Note that the package is not included. + * + * @param binaryName class name in binary form (ie, test.Foo$Bar) + * @return list of char arrays of class names, from outer to inner + */ + // @VisibleForTesting + static List<char[]> getClassChain(String binaryName) { + ArrayList<char[]> result = new ArrayList<char[]>(); + String className = BinaryName.getClassName(binaryName); + int idx; + while ((idx = className.indexOf('$')) >= 0) { + result.add(className.substring(0, idx).toCharArray()); + className = className.substring(idx + 1); + } + result.add(className.toCharArray()); + return result; + } + + /** + * Find a matching method in a type. + * + * @param type JDT method + * @param jMethod TypeOracle method object to find + * @return method declaration or null if not found + */ + private static AbstractMethodDeclaration findMethod(TypeDeclaration type, + JAbstractMethod jMethod) { + List<AbstractMethodDeclaration> candidates = findNamedMethods(type, + jMethod.getName()); + if (candidates.size() == 0) { + return null; + } + // TODO(jat): check overloads + assert candidates.size() == 1; + return candidates.get(0); + } + + /** + * Find all methods which have the requested name. + * + * <p>{...@code <clinit>} is not supported. + * @param type JDT type declaration + * @param name name of methods to find + * @return list of matching methods + */ + private static List<AbstractMethodDeclaration> findNamedMethods( + TypeDeclaration type, String name) { + List<AbstractMethodDeclaration> matching = new ArrayList<AbstractMethodDeclaration>(); + boolean isCtor = "<init>".equals(name); + char[] nameArray = name.toCharArray(); + for (AbstractMethodDeclaration method : type.methods) { + if ((isCtor && method.isConstructor()) || + (!isCtor && !method.isConstructor() && !method.isClinit() + && Arrays.equals(method.selector, nameArray))) { + matching.add(method); + } + } + return matching; + } + + /** + * Find a particular type in a compilation unit. + * + * @param unit JDT cud + * @param binaryName binary name of the type to find (ie, test.Foo$Bar) + * @return type declaration or null if not found + */ + private static TypeDeclaration findType(CompilationUnitDeclaration unit, + String binaryName) { + List<char[]> classChain = getClassChain(binaryName); + TypeDeclaration curType = findType(unit.types, classChain.get(0)); + for (int i = 1; i < classChain.size(); ++i) { + if (curType == null) { + return null; + } + curType = findType(curType.memberTypes, classChain.get(i)); + } + return curType; + } + + /** + * Find one type by name in a array of types. + * + * @param types array of types + * @param name name of type to find + * @return matching type or null if not found + */ + private static TypeDeclaration findType(TypeDeclaration[] types, char[] name) { + for (TypeDeclaration type : types) { + if (Arrays.equals(name, type.name)) { + return type; + } + } + return null; + } +} ======================================= --- /dev/null +++ /changes/jat/ihm/dev/core/test/com/google/gwt/dev/javac/JavaSourceParserTest.java Thu Jul 30 09:28:03 2009 @@ -0,0 +1,51 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed 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 com.google.gwt.dev.javac; + +import junit.framework.TestCase; + +import java.util.Arrays; +import java.util.List; + +/** + * Tests for {...@link JavaSourceParser}. + */ +public class JavaSourceParserTest extends TestCase { + + /** + * Test method for {...@link JavaSourceParser#getClassChain(java.lang.String)}. + */ + public void testGetClassChain() { + assertExpected(JavaSourceParser.getClassChain("Foo"), "Foo"); + assertExpected(JavaSourceParser.getClassChain("test.Foo"), "Foo"); + assertExpected(JavaSourceParser.getClassChain("Foo$Bar"), "Foo", "Bar"); + assertExpected(JavaSourceParser.getClassChain("test.Foo$Bar"), + "Foo", "Bar"); + assertExpected(JavaSourceParser.getClassChain("test.test2.Foo$Bar$Baz"), + "Foo", "Bar", "Baz"); + } + + private void assertExpected(List<char[]> actual, String... expected) { + if (actual.size() != expected.length) { + fail("Expected " + Arrays.deepToString(expected) + ", got " + actual); + } + for (int i = 0; i < expected.length; ++i) { + assertTrue("index " + i + " should be " + expected[i] + ", got " + + Arrays.toString(actual.get(i)), Arrays.equals(actual.get(i), + expected[i].toCharArray())); + } + } +} ======================================= --- /changes/jat/ihm/dev/core/src/com/google/gwt/core/ext/typeinfo/JAbstractMethod.java Wed Jul 29 15:12:22 2009 +++ /changes/jat/ihm/dev/core/src/com/google/gwt/core/ext/typeinfo/JAbstractMethod.java Thu Jul 30 09:28:03 2009 @@ -42,7 +42,7 @@ private List<JTypeParameter> typeParams = Lists.create(); - private boolean argNamesAreFake = false; + private String[] realParameterNames = null; JAbstractMethod(JAbstractMethod srcMethod) { this.annotations = new Annotations(srcMethod.annotations); @@ -161,10 +161,6 @@ public boolean isVarArgs() { return isVarArgs; } - - public void setFakeArgNames() { - argNamesAreFake = true; - } public void setVarArgs() { isVarArgs = true; @@ -242,6 +238,24 @@ Annotation[] getDeclaredAnnotations() { return annotations.getDeclaredAnnotations(); } + + // Called only by a JParameter, passing itself as a reference for lookup. + String getRealParameterName(JParameter parameter) { + if (realParameterNames == null) { + fetchRealParameterNames(); + // TODO(jat): handle null if not available + assert realParameterNames != null; + } + int n = params.size(); + for (int i = 0; i < n; ++i) { + // Identity tests are ok since identity is durable within an oracle. + if (params.get(i) == parameter) { + return realParameterNames[i]; + } + } + // TODO: report error if we are asked for an unknown JParameter? + return null; + } boolean hasParamTypes(JType[] paramTypes) { if (params.size() != paramTypes.length) { @@ -258,4 +272,13 @@ } return true; } -} + + private void fetchRealParameterNames() { + JClassType topLevelType = getEnclosingType(); + while (topLevelType.getEnclosingType() != null) { + topLevelType = topLevelType.getEnclosingType(); + } + realParameterNames = topLevelType.getRealParameterNames(getEnclosingType(), + this); + } +} ======================================= --- /changes/jat/ihm/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java Tue Jul 28 15:07:40 2009 +++ /changes/jat/ihm/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java Thu Jul 30 09:28:03 2009 @@ -115,7 +115,7 @@ * * @param lhsType * @param rhsType - * @return + * @return true if rhsType can be assigned to lhsType */ private static boolean areClassTypesAssignableNoSupers(JClassType lhsType, JClassType rhsType) { @@ -605,6 +605,19 @@ * NOTE: This method is for testing purposes only. */ abstract Annotation[] getDeclaredAnnotations(); + + /** + * Called by JAbstractMethod to lookup real argument names from source on + * demand (since bytecode does not always contain them). + * + * @param enclosingType + * @param abstractMethod + * @return array of parameter names, or null if unavailable + */ + String[] getRealParameterNames(JClassType enclosingType, + JAbstractMethod abstractMethod) { + return null; + } @Override abstract JClassType getSubstitutedType(JParameterizedType parameterizedType); ======================================= --- /changes/jat/ihm/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameter.java Tue Jun 16 14:33:06 2009 +++ /changes/jat/ihm/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameter.java Thu Jul 30 09:28:03 2009 @@ -26,11 +26,13 @@ private final Annotations annotations; - private final String name; + private String name; private JType type; private final JAbstractMethod enclosingMethod; + + private boolean argNameIsReal; public JParameter(JAbstractMethod enclosingMethod, JType type, String name) { @@ -39,9 +41,16 @@ public JParameter(JAbstractMethod enclosingMethod, JType type, String name, Map<Class<? extends Annotation>, Annotation> declaredAnnotations) { + this(enclosingMethod, type, name, declaredAnnotations, true); + } + + public JParameter(JAbstractMethod enclosingMethod, JType type, String name, + Map<Class<? extends Annotation>, Annotation> declaredAnnotations, + boolean argNameIsReal) { this.enclosingMethod = enclosingMethod; this.type = type; this.name = name; + this.argNameIsReal = argNameIsReal; enclosingMethod.addParameter(this); @@ -79,6 +88,10 @@ } public String getName() { + if (!argNameIsReal) { + name = enclosingMethod.getRealParameterName(this); + argNameIsReal = true; + } return name; } @@ -90,6 +103,7 @@ return annotations.isAnnotationPresent(annotationClass); } + @Override public String toString() { StringBuffer sb = new StringBuffer(); sb.append(type.getParameterizedQualifiedSourceName()); @@ -111,6 +125,11 @@ Annotation[] getDeclaredAnnotations() { return annotations.getDeclaredAnnotations(); } + + // Only called by JAbstractMethod after real parameter names are fetched. + void setName(String name) { + this.name = name; + } // Called when parameter types are found to be parameterized void setType(JType type) { ======================================= --- /changes/jat/ihm/dev/core/src/com/google/gwt/dev/javac/TypeOracleMediator.java Wed Jul 29 07:51:50 2009 +++ /changes/jat/ihm/dev/core/src/com/google/gwt/dev/javac/TypeOracleMediator.java Thu Jul 30 09:28:03 2009 @@ -1143,9 +1143,6 @@ if ((methodData.getAccess() & Opcodes.ACC_VARARGS) != 0) { method.setVarArgs(); } - if (!methodData.hasActualArgNames()) { - method.setFakeArgNames(); - } String signature = methodData.getSignature(); Type[] argTypes = methodData.getArgTypes(); @@ -1215,6 +1212,7 @@ CollectMethodData methodData) { Type[] argTypes = methodData.getArgTypes(); String[] argNames = methodData.getArgNames(); + boolean argNamesAreReal = methodData.hasActualArgNames(); List<CollectAnnotationData>[] paramAnnot = methodData.getArgAnnotations(); for (int i = 0; i < argTypes.length; ++i) { JType argType = resolveType(logger, argTypes[i]); @@ -1226,7 +1224,8 @@ resolveAnnotations(logger, paramAnnot[i], declaredAnnotations); // JParameter adds itself to the method - new JParameter(method, argType, argNames[i], declaredAnnotations); + new JParameter(method, argType, argNames[i], declaredAnnotations, + argNamesAreReal); } return true; } --~--~---------~--~----~------------~-------~--~----~ http://groups.google.com/group/Google-Web-Toolkit-Contributors -~----------~----~----~----~------~----~------~--~---
