FLAMING SWORD OF DEATH Review by: bruce (TBR) git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2742 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java index a480c17..a6dee8e 100644 --- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java +++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java
@@ -176,14 +176,12 @@ properties = Collections.unmodifiableSortedSet(mutableProperties); for (Script script : module.getScripts()) { - artifacts.add(new StandardScriptReference(script.getSrc(), - module.findPublicFile(script.getSrc()))); + artifacts.add(new StandardScriptReference(script.getSrc())); logger.log(TreeLogger.SPAM, "Added script " + script.getSrc(), null); } for (String style : module.getStyles()) { - artifacts.add(new StandardStylesheetReference(style, - module.findPublicFile(style))); + artifacts.add(new StandardStylesheetReference(style)); logger.log(TreeLogger.SPAM, "Added style " + style, null); }
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardPublicResource.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardPublicResource.java index 913580c..401b1b8 100644 --- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardPublicResource.java +++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardPublicResource.java
@@ -18,30 +18,24 @@ import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.linker.PublicResource; +import com.google.gwt.dev.resource.Resource; -import java.io.IOException; import java.io.InputStream; -import java.net.URL; /** * The standard implementation of {@link PublicResource}. */ public class StandardPublicResource extends PublicResource { - private final URL url; + private final Resource resource; - public StandardPublicResource(String partialPath, URL url) { + public StandardPublicResource(String partialPath, Resource resource) { super(StandardLinkerContext.class, partialPath); - this.url = url; + this.resource = resource; } @Override public InputStream getContents(TreeLogger logger) throws UnableToCompleteException { - try { - return url.openStream(); - } catch (IOException e) { - logger.log(TreeLogger.ERROR, "Unable to open file", e); - throw new UnableToCompleteException(); - } + return resource.openContents(); } }
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardScriptReference.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardScriptReference.java index 2410e12..79cc00a 100644 --- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardScriptReference.java +++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardScriptReference.java
@@ -17,18 +17,12 @@ import com.google.gwt.core.ext.linker.ScriptReference; -import java.net.URL; - /** * The standard implementation of {@link ScriptReference}. */ public class StandardScriptReference extends ScriptReference { - /** - * Might use <code>url</code>someday. - */ - @SuppressWarnings("unused") - public StandardScriptReference(String src, URL url) { + public StandardScriptReference(String src) { super(StandardLinkerContext.class, src); } }
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardStylesheetReference.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardStylesheetReference.java index 3e0540b..49211fa 100644 --- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardStylesheetReference.java +++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardStylesheetReference.java
@@ -17,18 +17,12 @@ import com.google.gwt.core.ext.linker.StylesheetReference; -import java.net.URL; - /** * The standard implementation of {@link StylesheetReference}. */ public class StandardStylesheetReference extends StylesheetReference { - /** - * Might use <code>url</code>someday. - */ - @SuppressWarnings("unused") - public StandardStylesheetReference(String src, URL url) { + public StandardStylesheetReference(String src) { super(StandardLinkerContext.class, src); } }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/CompilationUnitProvider.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/CompilationUnitProvider.java deleted file mode 100644 index 4d2aa8a..0000000 --- a/dev/core/src/com/google/gwt/core/ext/typeinfo/CompilationUnitProvider.java +++ /dev/null
@@ -1,51 +0,0 @@ -/* - * Copyright 2008 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.core.ext.typeinfo; - -import com.google.gwt.core.ext.UnableToCompleteException; - -import java.util.Comparator; - -/** - * Provides information about a single compilation unit on demand. - */ -public interface CompilationUnitProvider { - - Comparator<CompilationUnitProvider> LOCATION_COMPARATOR = new Comparator<CompilationUnitProvider>() { - public int compare(CompilationUnitProvider cups1, - CompilationUnitProvider cups2) { - String loc1 = cups1.getLocation(); - String loc2 = cups2.getLocation(); - return loc1.compareTo(loc2); - } - }; - - long getLastModified() throws UnableToCompleteException; - - String getLocation(); - - /** - * Returns the, not <code>null</code>, name of the top level public type. - * - */ - String getMainTypeName(); - - String getPackageName(); - - char[] getSource() throws UnableToCompleteException; - - boolean isTransient(); -}
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JAbstractMethod.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JAbstractMethod.java index 8d931c6..aa54e64 100644 --- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JAbstractMethod.java +++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JAbstractMethod.java
@@ -28,14 +28,6 @@ private final Annotations annotations; - private int bodyEnd; - - private int bodyStart; - - private final int declEnd; - - private final int declStart; - private boolean isVarArgs = false; private final HasMetaData metaData = new MetaData(); @@ -52,10 +44,6 @@ JAbstractMethod(JAbstractMethod srcMethod) { this.annotations = new Annotations(srcMethod.annotations); - this.bodyEnd = srcMethod.bodyEnd; - this.bodyStart = srcMethod.bodyStart; - this.declEnd = srcMethod.declEnd; - this.declStart = srcMethod.declStart; this.isVarArgs = srcMethod.isVarArgs; MetaData.copy(this, srcMethod.metaData); this.modifierBits = srcMethod.modifierBits; @@ -63,21 +51,16 @@ } // Only the builder can construct - JAbstractMethod(String name, int declStart, int declEnd, int bodyStart, - int bodyEnd, + JAbstractMethod(String name, Map<Class<? extends Annotation>, Annotation> declaredAnnotations, JTypeParameter[] jtypeParameters) { this.name = name; - this.declStart = declStart; - this.declEnd = declEnd; - this.bodyStart = bodyStart; - this.bodyEnd = bodyEnd; annotations = new Annotations(); annotations.addAnnotations(declaredAnnotations); - + if (jtypeParameters != null) { for (JTypeParameter jtypeParameter : jtypeParameters) { - addTypeParameter(jtypeParameter); + addTypeParameter(jtypeParameter); } } } @@ -108,22 +91,6 @@ return annotations.getAnnotation(annotationClass); } - public int getBodyEnd() { - return bodyEnd; - } - - public int getBodyStart() { - return bodyStart; - } - - public int getDeclEnd() { - return declEnd; - } - - public int getDeclStart() { - return declStart; - } - /** * Gets the type in which this method or constructor was declared. */ @@ -281,8 +248,8 @@ } return true; } - + private void addTypeParameter(JTypeParameter typeParameter) { typeParams.add(typeParameter); - } + } }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JAnnotationMethod.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JAnnotationMethod.java index 4b74e4f..4896191 100644 --- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JAnnotationMethod.java +++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JAnnotationMethod.java
@@ -29,11 +29,9 @@ private final Object defaultValue; public JAnnotationMethod(JClassType enclosingType, String name, - int declStart, int declEnd, int bodyStart, int bodyEnd, Object defaultValue, Map<Class<? extends Annotation>, Annotation> declaredAnnotations) { - super(enclosingType, name, declStart, declEnd, bodyStart, bodyEnd, - declaredAnnotations, null); + super(enclosingType, name, declaredAnnotations, null); this.defaultValue = defaultValue; }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JAnnotationType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JAnnotationType.java index fbec74e..912e20e 100644 --- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JAnnotationType.java +++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JAnnotationType.java
@@ -22,12 +22,11 @@ */ public class JAnnotationType extends JRealClassType { - public JAnnotationType(TypeOracle oracle, CompilationUnitProvider cup, - JPackage declaringPackage, JClassType enclosingType, boolean isLocalType, - String name, int declStart, int declEnd, int bodyStart, int bodyEnd, + public JAnnotationType(TypeOracle oracle, JPackage declaringPackage, + JClassType enclosingType, boolean isLocalType, String name, boolean isInterface) { - super(oracle, cup, declaringPackage, enclosingType, isLocalType, name, - declStart, declEnd, bodyStart, bodyEnd, isInterface); + super(oracle, declaringPackage, enclosingType, isLocalType, name, + isInterface); } @Override
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JArrayType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JArrayType.java index f66cd10..f238f41 100644 --- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JArrayType.java +++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JArrayType.java
@@ -15,8 +15,6 @@ */ package com.google.gwt.core.ext.typeinfo; -import com.google.gwt.core.ext.UnableToCompleteException; - import java.lang.annotation.Annotation; import java.util.Map; @@ -82,21 +80,6 @@ return null; } - @Override - public int getBodyEnd() { - return 0; - } - - @Override - public int getBodyStart() { - return 0; - } - - @Override - public CompilationUnitProvider getCompilationUnit() { - return null; - } - public JType getComponentType() { return componentType; } @@ -113,16 +96,6 @@ } @Override - public int getDeclEnd() { - return 0; - } - - @Override - public int getDeclStart() { - return 0; - } - - @Override public JClassType getEnclosingType() { return null; } @@ -272,20 +245,6 @@ } @Override - public String getTypeHash() throws UnableToCompleteException { - JType leafType = getLeafType(); - JClassType leafClassType = leafType.isClassOrInterface(); - if (leafClassType != null) { - return leafClassType.getTypeHash(); - } - - // Arrays of primitive types should have a stable hash - assert (leafType.isPrimitive() != null); - - return leafType.getQualifiedSourceName(); - } - - @Override public boolean isAbstract() { return false; }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java index 569e25d..eeb18ce 100644 --- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java +++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java
@@ -15,8 +15,6 @@ */ package com.google.gwt.core.ext.typeinfo; -import com.google.gwt.core.ext.UnableToCompleteException; - import java.lang.annotation.Annotation; import java.util.HashSet; import java.util.Map; @@ -338,21 +336,11 @@ public abstract <T extends Annotation> T getAnnotation( Class<T> annotationClass); - public abstract int getBodyEnd(); - - public abstract int getBodyStart(); - - public abstract CompilationUnitProvider getCompilationUnit(); - public abstract JConstructor getConstructor(JType[] paramTypes) throws NotFoundException; public abstract JConstructor[] getConstructors(); - public abstract int getDeclEnd(); - - public abstract int getDeclStart(); - public abstract JClassType getEnclosingType(); public abstract JClassType getErasedType(); @@ -412,8 +400,6 @@ public abstract JClassType getSuperclass(); - public abstract String getTypeHash() throws UnableToCompleteException; - public abstract boolean isAbstract(); public abstract boolean isAnnotationPresent(
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JConstructor.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JConstructor.java index a52b4e1..5767abc 100644 --- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JConstructor.java +++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JConstructor.java
@@ -24,18 +24,15 @@ public class JConstructor extends JAbstractMethod { private final JClassType enclosingType; - public JConstructor(JClassType enclosingType, String name, int declStart, - int declEnd, int bodyStart, int bodyEnd) { - this(enclosingType, name, declStart, declEnd, bodyStart, bodyEnd, null, null); + public JConstructor(JClassType enclosingType, String name) { + this(enclosingType, name, null, null); } - public JConstructor(JClassType enclosingType, String name, int declStart, - int declEnd, int bodyStart, int bodyEnd, + public JConstructor(JClassType enclosingType, String name, Map<Class<? extends Annotation>, Annotation> declaredAnnotations, JTypeParameter[] jtypeParameters) { - super(name, declStart, declEnd, bodyStart, bodyEnd, declaredAnnotations, - jtypeParameters); - + super(name, declaredAnnotations, jtypeParameters); + this.enclosingType = enclosingType; enclosingType.addConstructor(this); }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JDelegatingClassType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JDelegatingClassType.java index ad16306..3f3e0d4 100644 --- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JDelegatingClassType.java +++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JDelegatingClassType.java
@@ -15,8 +15,6 @@ */ package com.google.gwt.core.ext.typeinfo; -import com.google.gwt.core.ext.UnableToCompleteException; - import java.lang.annotation.Annotation; import java.util.Map; @@ -83,21 +81,6 @@ return baseType; } - @Override - public int getBodyEnd() { - return baseType.getBodyEnd(); - } - - @Override - public int getBodyStart() { - return baseType.getBodyStart(); - } - - @Override - public CompilationUnitProvider getCompilationUnit() { - return baseType.getCompilationUnit(); - } - /** * Delegating types generally cannot be constructed. */ @@ -116,16 +99,6 @@ } @Override - public int getDeclEnd() { - return baseType.getDeclEnd(); - } - - @Override - public int getDeclStart() { - return baseType.getDeclStart(); - } - - @Override public JClassType getEnclosingType() { // TODO this can be wrong if the enclosing type is a parameterized type. For // example, if a generic class has a non-static generic inner class. @@ -228,11 +201,6 @@ } @Override - public String getTypeHash() throws UnableToCompleteException { - return baseType.getTypeHash(); - } - - @Override public int hashCode() { return baseType.hashCode(); }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JEnumType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JEnumType.java index 6097392..7989c03 100644 --- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JEnumType.java +++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JEnumType.java
@@ -37,12 +37,11 @@ * @param bodyEnd * @param isInterface */ - public JEnumType(TypeOracle oracle, CompilationUnitProvider cup, - JPackage declaringPackage, JClassType enclosingType, boolean isLocalType, - String name, int declStart, int declEnd, int bodyStart, int bodyEnd, + public JEnumType(TypeOracle oracle, JPackage declaringPackage, + JClassType enclosingType, boolean isLocalType, String name, boolean isInterface) { - super(oracle, cup, declaringPackage, enclosingType, isLocalType, name, - declStart, declEnd, bodyStart, bodyEnd, isInterface); + super(oracle, declaringPackage, enclosingType, isLocalType, name, + isInterface); } /**
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JGenericType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JGenericType.java index d6a2173..a1286dc 100644 --- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JGenericType.java +++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JGenericType.java
@@ -27,16 +27,15 @@ private final List<JTypeParameter> typeParams = new ArrayList<JTypeParameter>(); - public JGenericType(TypeOracle oracle, CompilationUnitProvider cup, - JPackage declaringPackage, JClassType enclosingType, boolean isLocalType, - String name, int declStart, int declEnd, int bodyStart, int bodyEnd, + public JGenericType(TypeOracle oracle, JPackage declaringPackage, + JClassType enclosingType, boolean isLocalType, String name, boolean isInterface, JTypeParameter[] jtypeParameters) { - super(oracle, cup, declaringPackage, enclosingType, isLocalType, name, - declStart, declEnd, bodyStart, bodyEnd, isInterface); - - if (jtypeParameters != null) { + super(oracle, declaringPackage, enclosingType, isLocalType, name, + isInterface); + + if (jtypeParameters != null) { for (JTypeParameter jtypeParameter : jtypeParameters) { - addTypeParameter(jtypeParameter); + addTypeParameter(jtypeParameter); } } }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JMethod.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JMethod.java index 3bcdb7d..8de3b43 100644 --- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JMethod.java +++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JMethod.java
@@ -27,18 +27,14 @@ private JType returnType; - public JMethod(JClassType enclosingType, String name, int declStart, - int declEnd, int bodyStart, int bodyEnd) { - this(enclosingType, name, declStart, declEnd, bodyStart, bodyEnd, null, - null); + public JMethod(JClassType enclosingType, String name) { + this(enclosingType, name, null, null); } - public JMethod(JClassType enclosingType, String name, int declStart, - int declEnd, int bodyStart, int bodyEnd, + public JMethod(JClassType enclosingType, String name, Map<Class<? extends Annotation>, Annotation> declaredAnnotations, JTypeParameter[] jtypeParameters) { - super(name, declStart, declEnd, bodyStart, bodyEnd, declaredAnnotations, - jtypeParameters); + super(name, declaredAnnotations, jtypeParameters); this.enclosingType = enclosingType; enclosingType.addMethod(this); }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java index be91bc0..13329a8 100644 --- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java +++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java
@@ -15,10 +15,6 @@ */ package com.google.gwt.core.ext.typeinfo; -import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.dev.util.Util; - -import java.io.UnsupportedEncodingException; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.HashSet; @@ -35,18 +31,8 @@ private final Annotations annotations = new Annotations(); - private final int bodyEnd; - - private final int bodyStart; - - private final CompilationUnitProvider cup; - private final JPackage declaringPackage; - private final int declEnd; - - private final int declStart; - private final JClassType enclosingType; private final List<JClassType> interfaces = new ArrayList<JClassType>(); @@ -55,8 +41,6 @@ private final boolean isLocalType; - private String lazyHash; - private String lazyQualifiedName; private final AbstractMembers members = new Members(this); @@ -73,21 +57,14 @@ private JClassType superclass; - public JRealClassType(TypeOracle oracle, CompilationUnitProvider cup, - JPackage declaringPackage, JClassType enclosingType, boolean isLocalType, - String name, int declStart, int declEnd, int bodyStart, int bodyEnd, + public JRealClassType(TypeOracle oracle, JPackage declaringPackage, + JClassType enclosingType, boolean isLocalType, String name, boolean isInterface) { - oracle.recordTypeInCompilationUnit(cup, this); this.oracle = oracle; - this.cup = cup; this.declaringPackage = declaringPackage; this.enclosingType = enclosingType; this.isLocalType = isLocalType; this.name = name; - this.declStart = declStart; - this.declEnd = declEnd; - this.bodyStart = bodyStart; - this.bodyEnd = bodyEnd; this.isInterface = isInterface; if (enclosingType == null) { // Add myself to my package. @@ -155,18 +132,6 @@ return annotations.getAnnotation(annotationClass); } - public int getBodyEnd() { - return bodyEnd; - } - - public int getBodyStart() { - return bodyStart; - } - - public CompilationUnitProvider getCompilationUnit() { - return cup; - } - @Override public JConstructor getConstructor(JType[] paramTypes) throws NotFoundException { @@ -178,14 +143,6 @@ return members.getConstructors(); } - public int getDeclEnd() { - return declEnd; - } - - public int getDeclStart() { - return declStart; - } - public JClassType getEnclosingType() { return enclosingType; } @@ -298,19 +255,11 @@ return superclass; } - public String getTypeHash() throws UnableToCompleteException { - if (lazyHash == null) { - char[] source = cup.getSource(); - int length = declEnd - declStart + 1; - String s = new String(source, declStart, length); - try { - lazyHash = Util.computeStrongName(s.getBytes(Util.DEFAULT_ENCODING)); - } catch (UnsupportedEncodingException e) { - // Details, details... - throw new UnableToCompleteException(); - } - } - return lazyHash; + /** + * TODO: solve this better. + */ + public void invalidate() { + oracle.invalidate(this); } public boolean isAbstract() {
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java index 8105085..fdc7a64 100644 --- a/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java +++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java
@@ -143,7 +143,7 @@ * @return true if the type has been invalidated */ private static boolean isInvalidatedTypeRecursive(JType type, - Set<JClassType> invalidTypes) { + Set<JRealClassType> invalidTypes) { if (type instanceof JParameterizedType) { JParameterizedType parameterizedType = (JParameterizedType) type; if (isInvalidatedTypeRecursive(parameterizedType.getBaseType(), @@ -168,6 +168,8 @@ private final Map<JType, JArrayType> arrayTypes = new IdentityHashMap<JType, JArrayType>(); + private final Set<JRealClassType> invalidatedTypes = new HashSet<JRealClassType>(); + private JClassType javaLangObject; private final Map<String, JPackage> packages = new HashMap<String, JPackage>(); @@ -176,8 +178,6 @@ private int reloadCount = 0; - private final Map<CompilationUnitProvider, JClassType[]> typesByCup = new IdentityHashMap<CompilationUnitProvider, JClassType[]>(); - private final Map<String, List<JWildcardType>> wildcardTypes = new HashMap<String, List<JWildcardType>>(); public TypeOracle() { @@ -267,6 +267,7 @@ * <code>java.lang.Object</code>. */ public JClassType getJavaLangObject() { + assert javaLangObject != null; return javaLangObject; } @@ -347,7 +348,7 @@ throw new IllegalArgumentException("Generic type '" + genericType.getParameterizedQualifiedSourceName() + "' is a non-static member type, but the enclosing type '" - + enclosingType.getQualifiedSourceName() + + enclosingType.getQualifiedSourceName() + "' is not a parameterized or raw type"); } } @@ -459,22 +460,13 @@ JPackage pkg = pkgs[i]; JClassType[] types = pkg.getTypes(); for (int j = 0; j < types.length; j++) { - JClassType type = types[j]; + JRealClassType type = (JRealClassType) types[j]; buildAllTypesImpl(allTypes, type); } } return allTypes.toArray(NO_JCLASSES); } - public JClassType[] getTypesInCompilationUnit(CompilationUnitProvider cup) { - JClassType[] types = typesByCup.get(cup); - if (types != null) { - return types; - } else { - return NO_JCLASSES; - } - } - public JWildcardType getWildcardType(JWildcardType.BoundType boundType, JClassType typeBound) { JWildcardType wildcardType = new JWildcardType(boundType, typeBound); @@ -528,6 +520,42 @@ } /** + * Updates relationships within this type oracle. Should be called after any + * changes are made. + * + * <p> + * Throws <code>TypeOracleException</code> thrown if fundamental baseline + * correctness criteria are violated, most notably the absence of + * "java.lang.Object" + * </p> + * + * TODO: make this not public. + */ + public void refresh(TreeLogger logger) throws NotFoundException { + if (javaLangObject == null) { + javaLangObject = findType("java.lang.Object"); + if (javaLangObject == null) { + throw new NotFoundException("java.lang.Object"); + } + } + computeHierarchyRelationships(); + consumeTypeArgMetaData(logger); + } + + /** + * Removes all types that are no longer valid. + * + * TODO: make not public? + */ + public void removeInvalidatedTypes() { + if (!invalidatedTypes.isEmpty()) { + invalidateTypes(invalidatedTypes); + invalidatedTypes.clear(); + ++reloadCount; + } + } + + /** * Convenience method to sort class types in a consistent way. Note that the * order is subject to change and is intended to generate an "aesthetically * pleasing" order rather than a computationally reliable order. @@ -586,77 +614,16 @@ }); } - void incrementReloadCount() { - reloadCount++; + void invalidate(JRealClassType realClassType) { + invalidatedTypes.add(realClassType); } - /** - * Note, this method is called reflectively from the - * {@link com.google.gwt.dev.jdt.CacheManager#invalidateOnRefresh(TypeOracle)}. - * - * @param cup compilation unit whose types will be invalidated - */ - void invalidateTypesInCompilationUnit(CompilationUnitProvider cup) { - Set<JClassType> invalidTypes = new HashSet<JClassType>(); - JClassType[] types = typesByCup.get(cup); - if (types == null) { - return; - } - - for (int i = 0; i < types.length; i++) { - JClassType classTypeToInvalidate = types[i]; - invalidTypes.add(classTypeToInvalidate); - } - - typesByCup.remove(cup); - - removeInvalidatedArrayTypes(invalidTypes); - - removeInvalidatedParameterizedTypes(invalidTypes); - - removeTypes(invalidTypes); - } - - void recordTypeInCompilationUnit(CompilationUnitProvider cup, JClassType type) { - JClassType[] types = typesByCup.get(cup); - if (types == null) { - types = new JClassType[] {type}; - } else { - JClassType[] temp = new JClassType[types.length + 1]; - System.arraycopy(types, 0, temp, 0, types.length); - temp[types.length] = type; - types = temp; - } - typesByCup.put(cup, types); - } - - /** - * Updates relationships within this type oracle. Should be called after any - * changes are made. - * - * <p> - * Throws <code>TypeOracleException</code> thrown if fundamental baseline - * correctness criteria are violated, most notably the absence of - * "java.lang.Object" - * </p> - */ - void refresh(TreeLogger logger) throws NotFoundException { - if (javaLangObject == null) { - javaLangObject = findType("java.lang.Object"); - if (javaLangObject == null) { - throw new NotFoundException("java.lang.Object"); - } - } - computeHierarchyRelationships(); - consumeTypeArgMetaData(logger); - } - - private void buildAllTypesImpl(Set<JClassType> allTypes, JClassType type) { + private void buildAllTypesImpl(Set<JClassType> allTypes, JRealClassType type) { boolean didAdd = allTypes.add(type); assert (didAdd); JClassType[] nestedTypes = type.getNestedTypes(); for (int i = 0; i < nestedTypes.length; i++) { - JClassType nestedType = nestedTypes[i]; + JRealClassType nestedType = (JRealClassType) nestedTypes[i]; buildAllTypesImpl(allTypes, nestedType); } } @@ -843,6 +810,12 @@ return resultingType; } + private void invalidateTypes(Set<JRealClassType> invalidTypes) { + removeInvalidatedArrayTypes(invalidTypes); + removeInvalidatedParameterizedTypes(invalidTypes); + removeTypes(invalidTypes); + } + private JType parseImpl(String type) throws NotFoundException, ParseException, BadTypeArgsException { if (type.endsWith("[]")) { @@ -991,7 +964,7 @@ * * @param invalidTypes set of types that have been invalidated. */ - private void removeInvalidatedArrayTypes(Set<JClassType> invalidTypes) { + private void removeInvalidatedArrayTypes(Set<JRealClassType> invalidTypes) { arrayTypes.keySet().removeAll(invalidTypes); } @@ -1001,7 +974,8 @@ * * @param invalidTypes set of types known to have been invalidated */ - private void removeInvalidatedParameterizedTypes(Set<JClassType> invalidTypes) { + private void removeInvalidatedParameterizedTypes( + Set<JRealClassType> invalidTypes) { Iterator<List<JParameterizedType>> listIterator = parameterizedTypes.values().iterator(); while (listIterator.hasNext()) { @@ -1021,10 +995,8 @@ * * @param invalidTypes set of types to remove */ - private void removeTypes(Set<JClassType> invalidTypes) { - Iterator<JClassType> iter = invalidTypes.iterator(); - - while (iter.hasNext()) { + private void removeTypes(Set<JRealClassType> invalidTypes) { + for (Iterator<JRealClassType> iter = invalidTypes.iterator(); iter.hasNext();) { JClassType classType = iter.next(); JPackage pkg = classType.getPackage(); if (pkg != null) { @@ -1032,6 +1004,7 @@ } classType.removeFromSupertypes(); + iter.remove(); } } }
diff --git a/dev/core/src/com/google/gwt/dev/GWTCompiler.java b/dev/core/src/com/google/gwt/dev/GWTCompiler.java index a83801e..7505c9f 100644 --- a/dev/core/src/com/google/gwt/dev/GWTCompiler.java +++ b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
@@ -22,9 +22,7 @@ import com.google.gwt.core.ext.linker.SelectionProperty; import com.google.gwt.core.ext.linker.impl.StandardCompilationResult; import com.google.gwt.core.ext.linker.impl.StandardLinkerContext; -import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider; import com.google.gwt.core.ext.typeinfo.JClassType; -import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.dev.cfg.ModuleDef; import com.google.gwt.dev.cfg.ModuleDefLoader; import com.google.gwt.dev.cfg.Properties; @@ -32,9 +30,9 @@ import com.google.gwt.dev.cfg.PropertyPermutations; import com.google.gwt.dev.cfg.Rules; import com.google.gwt.dev.cfg.StaticPropertyOracle; -import com.google.gwt.dev.jdt.CacheManager; +import com.google.gwt.dev.javac.CompilationState; +import com.google.gwt.dev.javac.CompilationUnit; import com.google.gwt.dev.jdt.RebindPermutationOracle; -import com.google.gwt.dev.jdt.StandardSourceOracle; import com.google.gwt.dev.jdt.WebModeCompilerFrontEnd; import com.google.gwt.dev.jjs.JJSOptions; import com.google.gwt.dev.jjs.JavaToJavaScriptCompiler; @@ -122,8 +120,8 @@ private final Map<String, String> cache = new HashMap<String, String>(); public CompilationRebindOracle(ArtifactSet generatorArtifacts) { - super(typeOracle, propOracle, module, rules, genDir, - generatorResourcesDir, cacheManager, generatorArtifacts); + super(compilationState, propOracle, module, rules, genDir, + generatorResourcesDir, generatorArtifacts); } /** @@ -146,31 +144,8 @@ String msg = "Rebind answer for '" + in + "' found in cache " + out; logger.log(TreeLogger.DEBUG, msg, null); } - - if (recordDecision(in, out)) { - List<JClassType> genTypes = generatedTypesByResultTypeName.get(out); - if (genTypes != null) { - for (JClassType genType : genTypes) { - String sourceHash = genType.getTypeHash(); - String genTypeName = genType.getQualifiedSourceName(); - recordGeneratedTypeHash(genTypeName, sourceHash); - } - } - } - return out; } - - @SuppressWarnings("unused") - protected boolean recordDecision(String in, String out) { - // TODO(bobv): consider caching compilations again? - return false; - } - - @SuppressWarnings("unused") - protected void recordGeneratedTypeHash(String typeName, String sourceHash) { - // TODO(bobv): consider caching compilations again? - } } private class DistillerRebindPermutationOracle implements @@ -179,9 +154,8 @@ private final StandardRebindOracle rebindOracle; public DistillerRebindPermutationOracle(ArtifactSet generatorArtifacts) { - rebindOracle = new StandardRebindOracle(typeOracle, propOracle, module, - rules, genDir, generatorResourcesDir, cacheManager, - generatorArtifacts) { + rebindOracle = new StandardRebindOracle(compilationState, propOracle, + module, rules, genDir, generatorResourcesDir, generatorArtifacts) { /** * Record generated types. @@ -246,20 +220,7 @@ System.exit(1); } - /** - * Returns the fully-qualified main type name of a compilation unit. - */ - private static String makeTypeName(CompilationUnitProvider cup) { - if (cup.getPackageName().length() > 0) { - return cup.getPackageName() + "." + cup.getMainTypeName(); - } else { - return cup.getMainTypeName(); - } - } - - private final CacheManager cacheManager; - - private File generatorResourcesDir; + private CompilationState compilationState; private String[] declEntryPts; @@ -267,6 +228,8 @@ private Map<String, List<JClassType>> generatedTypesByResultTypeName = new HashMap<String, List<JClassType>>(); + private File generatorResourcesDir; + private JavaToJavaScriptCompiler jjs; private JJSOptions jjsOptions = new JJSOptions(); @@ -289,17 +252,9 @@ private Rules rules; - private StandardSourceOracle sourceOracle; - - private TypeOracle typeOracle; - private boolean useGuiLogger; public GWTCompiler() { - this(null); - } - - public GWTCompiler(CacheManager cacheManager) { registerHandler(new ArgHandlerLogLevel() { @Override public void setLogLevel(Type level) { @@ -344,13 +299,12 @@ }); registerHandler(new ArgHandlerValidateOnlyFlag()); - - this.cacheManager = cacheManager; } public void distill(TreeLogger logger, ModuleDef moduleDef) throws UnableToCompleteException { this.module = moduleDef; + this.compilationState = moduleDef.getCompilationState(); // Set up all the initial state. checkModule(logger); @@ -369,18 +323,20 @@ Util.recursiveDelete(generatorResourcesDir, true); generatorResourcesDir.mkdirs(); + // TODO: All JDT checks now before even building TypeOracle? + compilationState.compile(logger); + rules = module.getRules(); - typeOracle = module.getTypeOracle(logger); - sourceOracle = new StandardSourceOracle(typeOracle); if (jjsOptions.isValidateOnly()) { + // TODO: revisit this.. do we even need to run JJS? logger.log(TreeLogger.INFO, "Validating compilation " + module.getName(), null); // Pretend that every single compilation unit is an entry point. - CompilationUnitProvider[] compilationUnits = module.getCompilationUnits(); - declEntryPts = new String[compilationUnits.length]; - for (int i = 0; i < compilationUnits.length; ++i) { - CompilationUnitProvider cup = compilationUnits[i]; - declEntryPts[i] = makeTypeName(cup); + Set<CompilationUnit> compilationUnits = compilationState.getCompilationUnits(); + declEntryPts = new String[compilationUnits.size()]; + int i = 0; + for (CompilationUnit unit : compilationUnits) { + declEntryPts[i++] = unit.getTypeName(); } } else { logger.log(TreeLogger.INFO, "Compiling module " + module.getName(), null); @@ -393,7 +349,7 @@ properties = module.getProperties(); perms = new PropertyPermutations(properties); WebModeCompilerFrontEnd frontEnd = new WebModeCompilerFrontEnd( - sourceOracle, rebindPermOracle); + compilationState, rebindPermOracle); jjs = new JavaToJavaScriptCompiler(logger, frontEnd, declEntryPts, jjsOptions);
diff --git a/dev/core/src/com/google/gwt/dev/GWTShell.java b/dev/core/src/com/google/gwt/dev/GWTShell.java index f22c3ef..61ba6ad 100644 --- a/dev/core/src/com/google/gwt/dev/GWTShell.java +++ b/dev/core/src/com/google/gwt/dev/GWTShell.java
@@ -160,27 +160,6 @@ } /** - * handles the -saveJsni command line flag. - */ - protected class ArgHandlerSaveJsni extends ArgHandlerFlag { - @Override - public String getPurpose() { - return "Save generated JSNI source in the supplied gen directory (if any)"; - } - - @Override - public String getTag() { - return "-saveJsni"; - } - - @Override - public boolean setFlag() { - saveJsni = true; - return true; - } - } - - /** * Handles the list of startup urls that can be passed on the command line. */ protected class ArgHandlerStartupURLs extends ArgHandlerExtra { @@ -403,8 +382,6 @@ private boolean runTomcat = true; - private boolean saveJsni = false; - private boolean started; private final List<String> startupUrls = new ArrayList<String>(); @@ -442,8 +419,6 @@ } }); - registerHandler(new ArgHandlerSaveJsni()); - if (!noURLs) { registerHandler(new ArgHandlerStartupURLs()); } @@ -644,7 +619,7 @@ */ protected void compile(TreeLogger logger, ModuleDef moduleDef) throws UnableToCompleteException { - GWTCompiler compiler = new GWTCompiler(moduleDef.getCacheManager()); + GWTCompiler compiler = new GWTCompiler(); compiler.setCompilerOptions(jjsOptions); compiler.setGenDir(genDir); compiler.setOutDir(outDir); @@ -668,7 +643,7 @@ TreeLogger logger, TypeOracle typeOracle, ModuleDef moduleDef, File genDir, File shellDir) { return new ShellModuleSpaceHost(logger, typeOracle, moduleDef, genDir, - shellDir, saveJsni); + shellDir); } /**
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java b/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java index e716af6..9cddd07 100644 --- a/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java +++ b/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
@@ -20,22 +20,23 @@ import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.linker.LinkerOrder; import com.google.gwt.core.ext.linker.LinkerOrder.Order; -import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider; import com.google.gwt.core.ext.typeinfo.TypeOracle; -import com.google.gwt.dev.jdt.CacheManager; -import com.google.gwt.dev.jdt.TypeOracleBuilder; -import com.google.gwt.dev.jdt.URLCompilationUnitProvider; +import com.google.gwt.dev.javac.CompilationState; +import com.google.gwt.dev.javac.JavaSourceFile; +import com.google.gwt.dev.javac.JavaSourceOracle; +import com.google.gwt.dev.javac.impl.JavaSourceOracleImpl; +import com.google.gwt.dev.resource.Resource; +import com.google.gwt.dev.resource.impl.PathPrefix; +import com.google.gwt.dev.resource.impl.PathPrefixSet; +import com.google.gwt.dev.resource.impl.ResourceFilter; +import com.google.gwt.dev.resource.impl.ResourceOracleImpl; import com.google.gwt.dev.util.Empty; -import com.google.gwt.dev.util.FileOracle; -import com.google.gwt.dev.util.FileOracleFactory; import com.google.gwt.dev.util.PerfLogger; import com.google.gwt.dev.util.Util; -import com.google.gwt.dev.util.FileOracleFactory.FileFilter; import org.apache.tools.ant.types.ZipScanner; import java.io.File; -import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; @@ -84,22 +85,17 @@ private Class<? extends Linker> activePrimaryLinker; - private final ArrayList<URLCompilationUnitProvider> allCups = new ArrayList<URLCompilationUnitProvider>(); - - private final Set<String> alreadySeenFiles = new HashSet<String>(); - - private final CacheManager cacheManager = new CacheManager(".gwt-cache", - new TypeOracle()); - - private CompilationUnitProvider[] cups = new CompilationUnitProvider[0]; + private CompilationState compilationState; private final List<String> entryPointTypeNames = new ArrayList<String>(); private final Set<File> gwtXmlFiles = new HashSet<File>(); - private FileOracle lazyPublicOracle; + private JavaSourceOracle lazyJavaSourceOracle; - private FileOracle lazySourceOracle; + private ResourceOracleImpl lazyPublicOracle; + + private ResourceOracleImpl lazySourceOracle; private TypeOracle lazyTypeOracle; @@ -117,7 +113,7 @@ private final Properties properties = new Properties(); - private final FileOracleFactory publicPathEntries = new FileOracleFactory(); + private PathPrefixSet publicPrefixSet = new PathPrefixSet(); private final Rules rules = new Rules(); @@ -125,7 +121,7 @@ private final Map<String, String> servletClassNamesByPath = new HashMap<String, String>(); - private final FileOracleFactory sourcePathEntries = new FileOracleFactory(); + private PathPrefixSet sourcePrefixSet = new PathPrefixSet(); private final Styles styles = new Styles(); @@ -164,12 +160,11 @@ final ZipScanner scanner = getScanner(includeList, excludeList, defaultExcludes, caseSensitive); - // index from this package down - publicPathEntries.addRootPackage(publicPackage, new FileFilter() { - public boolean accept(String name) { - return scanner.match(name); + publicPrefixSet.add(new PathPrefix(publicPackage, new ResourceFilter() { + public boolean allows(String path) { + return scanner.match(path); } - }); + }, true)); } public void addSourcePackage(String sourcePackage, String[] includeList, @@ -195,17 +190,15 @@ final ZipScanner scanner = getScanner(includeList, excludeList, defaultExcludes, caseSensitive); - final FileFilter sourceFileFilter = new FileFilter() { - public boolean accept(String name) { - return scanner.match(name); + ResourceFilter sourceFileFilter = new ResourceFilter() { + public boolean allows(String path) { + return path.endsWith(".java") && scanner.match(path); } }; - if (isSuperSource) { - sourcePathEntries.addRootPackage(sourcePackage, sourceFileFilter); - } else { - sourcePathEntries.addPackage(sourcePackage, sourceFileFilter); - } + PathPrefix pathPrefix = new PathPrefix(sourcePackage, sourceFileFilter, + isSuperSource); + sourcePrefixSet.add(pathPrefix); } public void addSuperSourcePackage(String superSourcePackage, @@ -223,13 +216,12 @@ linkerTypesByName.put(name, linker); } - public synchronized URL findPublicFile(String partialPath) { - return lazyPublicOracle.find(partialPath); + public synchronized Resource findPublicFile(String partialPath) { + return lazyPublicOracle.getResourceMap().get(partialPath); } public synchronized String findServletForPath(String actual) { // Walk in backwards sorted order to find the longest path match first. - // Set<Entry<String, String>> entrySet = servletClassNamesByPath.entrySet(); Entry<String, String>[] entries = Util.toArray(Entry.class, entrySet); Arrays.sort(entries, REV_NAME_CMP); @@ -257,11 +249,7 @@ } public String[] getAllPublicFiles() { - return lazyPublicOracle.getAllFiles(); - } - - public CacheManager getCacheManager() { - return cacheManager; + return lazyPublicOracle.getPathNames().toArray(Empty.STRINGS); } /** @@ -272,8 +260,8 @@ return name; } - public synchronized CompilationUnitProvider[] getCompilationUnits() { - return cups; + public CompilationState getCompilationState() { + return compilationState; } public synchronized String[] getEntryPointTypeNames() { @@ -332,7 +320,8 @@ public synchronized TypeOracle getTypeOracle(TreeLogger logger) throws UnableToCompleteException { if (lazyTypeOracle == null) { - lazyTypeOracle = createTypeOracle(logger); + lazyTypeOracle = compilationState.getTypeOracle(); + updateTypeOracle(logger); } return lazyTypeOracle; } @@ -363,11 +352,17 @@ public synchronized void refresh(TreeLogger logger) throws UnableToCompleteException { PerfLogger.start("ModuleDef.refresh"); - cacheManager.invalidateVolatileFiles(); - normalize(logger); - lazyTypeOracle = createTypeOracle(logger); - Util.invokeInaccessableMethod(TypeOracle.class, "incrementReloadCount", - new Class[] {}, lazyTypeOracle, new Object[] {}); + + // Refresh the public path. + lazyPublicOracle.refresh(logger); + + // Refreshes source internally. + compilationState.refresh(); + + // Refresh type oracle if needed. + if (lazyTypeOracle != null) { + updateTypeOracle(logger); + } PerfLogger.end(); } @@ -388,8 +383,8 @@ * @param partialPath * @return */ - synchronized URL findSourceFile(String partialPath) { - return lazySourceOracle.find(partialPath); + synchronized JavaSourceFile findSourceFile(String partialPath) { + return lazyJavaSourceOracle.getSourceMap().get(partialPath); } /** @@ -424,111 +419,32 @@ } } - // Create the source path. - // - TreeLogger branch = Messages.SOURCE_PATH_LOCATIONS.branch(logger, null); - lazySourceOracle = sourcePathEntries.create(branch); + // Create the public path. + TreeLogger branch = Messages.PUBLIC_PATH_LOCATIONS.branch(logger, null); + // lazyPublicOracle = publicPathEntries.create(branch); + if (lazyPublicOracle == null) { + lazyPublicOracle = new ResourceOracleImpl(branch); + lazyPublicOracle.setPathPrefixes(publicPrefixSet); + } + lazyPublicOracle.refresh(branch); - if (lazySourceOracle.isEmpty()) { + // Create the source path. + branch = Messages.SOURCE_PATH_LOCATIONS.branch(logger, null); + lazySourceOracle = new ResourceOracleImpl(branch); + lazySourceOracle.setPathPrefixes(sourcePrefixSet); + lazySourceOracle.refresh(branch); + if (lazySourceOracle.getResources().isEmpty()) { branch.log(TreeLogger.WARN, "No source path entries; expect subsequent failures", null); - } else { - // Create the CUPs - String[] allFiles = lazySourceOracle.getAllFiles(); - Set<String> files = new HashSet<String>(); - files.addAll(Arrays.asList(allFiles)); - files.removeAll(alreadySeenFiles); - for (Iterator<String> iter = files.iterator(); iter.hasNext();) { - String fileName = iter.next(); - int pos = fileName.lastIndexOf('/'); - String packageName; - if (pos >= 0) { - packageName = fileName.substring(0, pos); - packageName = packageName.replace('/', '.'); - } else { - packageName = ""; - } - URL url = lazySourceOracle.find(fileName); - allCups.add(new URLCompilationUnitProvider(url, packageName)); - } - alreadySeenFiles.addAll(files); - this.cups = allCups.toArray(this.cups); } + lazyJavaSourceOracle = new JavaSourceOracleImpl(lazySourceOracle); - // Create the public path. - // - branch = Messages.PUBLIC_PATH_LOCATIONS.branch(logger, null); - lazyPublicOracle = publicPathEntries.create(branch); + // Create the compilation state. + compilationState = new CompilationState(lazyJavaSourceOracle); PerfLogger.end(); } - private TypeOracle createTypeOracle(TreeLogger logger) - throws UnableToCompleteException { - - TypeOracle newTypeOracle = null; - - try { - String msg = "Analyzing source in module '" + getName() + "'"; - TreeLogger branch = logger.branch(TreeLogger.TRACE, msg, null); - long before = System.currentTimeMillis(); - TypeOracleBuilder builder = new TypeOracleBuilder(getCacheManager()); - CompilationUnitProvider[] currentCups = getCompilationUnits(); - Arrays.sort(currentCups, CompilationUnitProvider.LOCATION_COMPARATOR); - - TreeLogger subBranch = null; - if (branch.isLoggable(TreeLogger.DEBUG)) { - subBranch = branch.branch(TreeLogger.DEBUG, - "Adding compilation units...", null); - } - - for (int i = 0; i < currentCups.length; i++) { - CompilationUnitProvider cup = currentCups[i]; - if (subBranch != null) { - subBranch.log(TreeLogger.DEBUG, cup.getLocation(), null); - } - builder.addCompilationUnit(currentCups[i]); - } - - if (lazyTypeOracle != null) { - cacheManager.invalidateOnRefresh(lazyTypeOracle); - } - - newTypeOracle = builder.build(branch); - long after = System.currentTimeMillis(); - branch.log(TreeLogger.TRACE, "Finished in " + (after - before) + " ms", - null); - } catch (UnableToCompleteException e) { - logger.log(TreeLogger.ERROR, "Failed to complete analysis", null); - throw new UnableToCompleteException(); - } - - // Sanity check the seed types and don't even start it they're missing. - // - boolean seedTypesMissing = false; - if (newTypeOracle.findType("java.lang.Object") == null) { - Util.logMissingTypeErrorWithHints(logger, "java.lang.Object"); - seedTypesMissing = true; - } else { - TreeLogger branch = logger.branch(TreeLogger.TRACE, - "Finding entry point classes", null); - String[] typeNames = getEntryPointTypeNames(); - for (int i = 0; i < typeNames.length; i++) { - String typeName = typeNames[i]; - if (newTypeOracle.findType(typeName) == null) { - Util.logMissingTypeErrorWithHints(branch, typeName); - seedTypesMissing = true; - } - } - } - - if (seedTypesMissing) { - throw new UnableToCompleteException(); - } - - return newTypeOracle; - } - private ZipScanner getScanner(String[] includeList, String[] excludeList, boolean defaultExcludes, boolean caseSensitive) { /* @@ -552,4 +468,37 @@ return scanner; } + private void updateTypeOracle(TreeLogger logger) + throws UnableToCompleteException { + PerfLogger.start("ModuleDef.updateTypeOracle"); + TreeLogger branch = logger.branch(TreeLogger.TRACE, + "Compiling Java source files in module '" + getName() + "'"); + compilationState.compile(branch); + PerfLogger.end(); + + TypeOracle typeOracle = compilationState.getTypeOracle(); + + // Sanity check the seed types and don't even start it they're missing. + boolean seedTypesMissing = false; + if (typeOracle.findType("java.lang.Object") == null) { + Util.logMissingTypeErrorWithHints(logger, "java.lang.Object"); + seedTypesMissing = true; + } else { + branch = logger.branch(TreeLogger.TRACE, "Finding entry point classes", + null); + String[] typeNames = getEntryPointTypeNames(); + for (int i = 0; i < typeNames.length; i++) { + String typeName = typeNames[i]; + if (typeOracle.findType(typeName) == null) { + Util.logMissingTypeErrorWithHints(branch, typeName); + seedTypesMissing = true; + } + } + } + + if (seedTypesMissing) { + throw new UnableToCompleteException(); + } + } + }
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ModuleDefLoader.java b/dev/core/src/com/google/gwt/dev/cfg/ModuleDefLoader.java index 5cb3086..f61e01c 100644 --- a/dev/core/src/com/google/gwt/dev/cfg/ModuleDefLoader.java +++ b/dev/core/src/com/google/gwt/dev/cfg/ModuleDefLoader.java
@@ -101,7 +101,7 @@ */ public static ModuleDef loadFromClassPath(TreeLogger logger, String moduleName) throws UnableToCompleteException { - return loadFromClassPath(logger, moduleName, true); + return loadFromClassPath(logger, moduleName, false); } /**
diff --git a/dev/core/src/com/google/gwt/dev/cfg/PublicOracle.java b/dev/core/src/com/google/gwt/dev/cfg/PublicOracle.java index 3709608..c88f852 100644 --- a/dev/core/src/com/google/gwt/dev/cfg/PublicOracle.java +++ b/dev/core/src/com/google/gwt/dev/cfg/PublicOracle.java
@@ -1,5 +1,5 @@ /* - * Copyright 2007 Google Inc. + * Copyright 2008 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 @@ -15,7 +15,7 @@ */ package com.google.gwt.dev.cfg; -import java.net.URL; +import com.google.gwt.dev.resource.Resource; /** * Abstracts the process of querying for public files. @@ -28,7 +28,7 @@ * @param partialPath a file path relative to the root of any public package * @return the url of the file, or <code>null</code> if no such file exists */ - URL findPublicFile(String partialPath); + Resource findPublicFile(String partialPath); /** * Returns all available public files.
diff --git a/dev/core/src/com/google/gwt/dev/jdt/AnnotationProxyFactory.java b/dev/core/src/com/google/gwt/dev/javac/AnnotationProxyFactory.java similarity index 98% rename from dev/core/src/com/google/gwt/dev/jdt/AnnotationProxyFactory.java rename to dev/core/src/com/google/gwt/dev/javac/AnnotationProxyFactory.java index d3ae321..28627e7 100644 --- a/dev/core/src/com/google/gwt/dev/jdt/AnnotationProxyFactory.java +++ b/dev/core/src/com/google/gwt/dev/javac/AnnotationProxyFactory.java
@@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.google.gwt.dev.jdt; +package com.google.gwt.dev.javac; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationHandler; @@ -284,7 +284,7 @@ AnnotationProxyInvocationHandler annotationInvocationHandler = new AnnotationProxyInvocationHandler( identifierToValue, annotationClass); Annotation proxy = (Annotation) Proxy.newProxyInstance( - AnnotationProxyFactory.class.getClassLoader(), new Class<?>[] { + Thread.currentThread().getContextClassLoader(), new Class<?>[] { java.lang.annotation.Annotation.class, annotationClass}, annotationInvocationHandler); annotationInvocationHandler.setProxy(proxy);
diff --git a/dev/core/src/com/google/gwt/dev/jdt/BinaryTypeReferenceRestrictionsChecker.java b/dev/core/src/com/google/gwt/dev/javac/BinaryTypeReferenceRestrictionsChecker.java similarity index 85% rename from dev/core/src/com/google/gwt/dev/jdt/BinaryTypeReferenceRestrictionsChecker.java rename to dev/core/src/com/google/gwt/dev/javac/BinaryTypeReferenceRestrictionsChecker.java index 8d4382a..3fc0ba4 100644 --- a/dev/core/src/com/google/gwt/dev/jdt/BinaryTypeReferenceRestrictionsChecker.java +++ b/dev/core/src/com/google/gwt/dev/javac/BinaryTypeReferenceRestrictionsChecker.java
@@ -13,7 +13,9 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.google.gwt.dev.jdt; +package com.google.gwt.dev.javac; + +import com.google.gwt.dev.jdt.TypeRefVisitor; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.Expression; @@ -33,13 +35,13 @@ * Check a {@link CompilationUnitDeclaration} for references to binary types * outside the context of an annotation. */ -class BinaryTypeReferenceRestrictionsChecker { +public class BinaryTypeReferenceRestrictionsChecker { /** * Records the location from which a {@link BinaryTypeBinding} is referenced. */ static class BinaryTypeReferenceSite { - private final Expression expression; private final BinaryTypeBinding binaryTypeBinding; + private final Expression expression; BinaryTypeReferenceSite(Expression expression, BinaryTypeBinding binaryTypeBinding) { @@ -107,17 +109,24 @@ * is reported against the {@link CompilationUnitDeclaration} for the first * instance of each unique {@link BinaryTypeBinding}. */ - public static void check(CompilationUnitDeclaration cud) { - List<BinaryTypeReferenceSite> binaryTypeReferenceSites = findInvalidBinaryTypeReferenceSites(cud); - Set<BinaryTypeBinding> invalidBindaryTypeBindings = new HashSet<BinaryTypeBinding>(); + public static void check(CompilationUnitDeclaration cud, + Set<String> validBinaryTypeNames) { + List<BinaryTypeReferenceSite> binaryTypeReferenceSites = findAllBinaryTypeReferenceSites(cud); + Set<BinaryTypeBinding> alreadySeenTypeBindings = new HashSet<BinaryTypeBinding>(); for (BinaryTypeReferenceSite binaryTypeReferenceSite : binaryTypeReferenceSites) { BinaryTypeBinding binaryTypeBinding = binaryTypeReferenceSite.getBinaryTypeBinding(); - if (invalidBindaryTypeBindings.contains(binaryTypeBinding)) { + if (alreadySeenTypeBindings.contains(binaryTypeBinding)) { continue; } - invalidBindaryTypeBindings.add(binaryTypeBinding); + alreadySeenTypeBindings.add(binaryTypeBinding); + String binaryName = String.valueOf(binaryTypeBinding.constantPoolName()); + if (validBinaryTypeNames.contains(binaryName)) { + // This binary name is valid; it is a reference to a unit that was + // compiled in a previous JDT run. + continue; + } String qualifiedTypeName = binaryTypeBinding.debugName(); String error = formatBinaryTypeRefErrorMessage(qualifiedTypeName); @@ -127,7 +136,7 @@ } } - static List<BinaryTypeReferenceSite> findInvalidBinaryTypeReferenceSites( + static List<BinaryTypeReferenceSite> findAllBinaryTypeReferenceSites( CompilationUnitDeclaration cud) { List<BinaryTypeReferenceSite> binaryTypeReferenceSites = new ArrayList<BinaryTypeReferenceSite>(); BinaryTypeReferenceVisitor binaryTypeReferenceVisitor = new BinaryTypeReferenceVisitor(
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationState.java b/dev/core/src/com/google/gwt/dev/javac/CompilationState.java new file mode 100644 index 0000000..bdf9c2c --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/javac/CompilationState.java
@@ -0,0 +1,194 @@ +/* + * Copyright 2008 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.TreeLogger; +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.core.ext.typeinfo.TypeOracle; +import com.google.gwt.dev.javac.CompilationUnit.State; +import com.google.gwt.dev.javac.impl.SourceFileCompilationUnit; +import com.google.gwt.dev.js.ast.JsProgram; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * Encapsulates the state of active compilation units in a particular module. + * State is accumulated throughout the life cycle of the containing module and + * may be invalidated at certain times and recomputed. + */ +public class CompilationState { + protected final Map<String, CompilationUnit> unitMap = new HashMap<String, CompilationUnit>(); + private Set<JavaSourceFile> cachedSourceFiles = Collections.emptySet(); + private Map<String, CompiledClass> exposedClassFileMap = null; + private final Map<String, CompilationUnit> exposedUnitMap = Collections.unmodifiableMap(unitMap); + private Set<CompilationUnit> exposedUnits = Collections.emptySet(); + private final TypeOracleMediator mediator = new TypeOracleMediator(); + private final JavaSourceOracle sourceOracle; + + /** + * Construct a new {@link CompilationState}. + * + * @param sourceOracle an oracle used to retrieve source code and check for + * changes in the underlying source code base + */ + public CompilationState(JavaSourceOracle sourceOracle) { + this.sourceOracle = sourceOracle; + refresh(); + } + + public void addGeneratedCompilationUnit(CompilationUnit unit) { + String typeName = unit.getTypeName(); + assert (!unitMap.containsKey(typeName)); + unitMap.put(typeName, unit); + updateExposedUnits(); + } + + /** + * Compile all units and updates all internal state. Invalidate any units with + * compile errors. + */ + public void compile(TreeLogger logger) throws UnableToCompleteException { + JdtCompiler.compile(getCompilationUnits()); + CompilationUnitInvalidator.validateCompilationUnits(getCompilationUnits(), + getClassFileMap()); + + // TODO: Move into validation & log errors? + JsniCollector.collectJsniMethods(logger, getCompilationUnits(), + new JsProgram()); + + CompilationUnitInvalidator.invalidateUnitsWithErrors(logger, + getCompilationUnits()); + + mediator.refresh(logger, getCompilationUnits()); + + // Any surviving units are now checked. + for (CompilationUnit unit : getCompilationUnits()) { + if (unit.getState() == State.COMPILED) { + unit.setState(State.CHECKED); + } + } + + updateExposedUnits(); + } + + /** + * Returns a map of all compiled classes by binary name. + */ + public Map<String, CompiledClass> getClassFileMap() { + if (exposedClassFileMap == null) { + HashMap<String, CompiledClass> classFileMap = new HashMap<String, CompiledClass>(); + for (CompilationUnit unit : getCompilationUnits()) { + if (unit.isCompiled()) { + for (CompiledClass compiledClass : unit.getCompiledClasses()) { + classFileMap.put(compiledClass.getBinaryName(), compiledClass); + } + } + } + exposedClassFileMap = Collections.unmodifiableMap(classFileMap); + } + return exposedClassFileMap; + } + + /** + * Returns an unmodifiable view of the set of compilation units, mapped by the + * main type's qualified source name. + */ + public Map<String, CompilationUnit> getCompilationUnitMap() { + return exposedUnitMap; + } + + /** + * Returns an unmodifiable view of the set of compilation units. + */ + public Set<CompilationUnit> getCompilationUnits() { + return exposedUnits; + } + + public TypeOracle getTypeOracle() { + return mediator.getTypeOracle(); + } + + /** + * Synchronize against the source oracle to check for added/removed/updated + * units. Updated units are invalidated, and any units depending on changed + * units are also invalidated. All generated units are removed. + * + * TODO: something more optimal with generated files? + */ + public void refresh() { + // Always remove all generated compilation units. + for (Iterator<CompilationUnit> it = unitMap.values().iterator(); it.hasNext();) { + CompilationUnit unit = it.next(); + if (unit.isGenerated()) { + unit.setState(State.FRESH); + it.remove(); + } + } + + refreshFromSourceOracle(); + // Don't log about invalidated units via refresh. + CompilationUnitInvalidator.invalidateUnitsWithInvalidRefs(TreeLogger.NULL, + getCompilationUnits()); + updateExposedUnits(); + } + + private void refreshFromSourceOracle() { + // See if the source oracle has changed. + Set<JavaSourceFile> newSourceFiles = sourceOracle.getSourceFiles(); + if (cachedSourceFiles == newSourceFiles) { + return; + } + + // Divide resources into changed and unchanged. + Set<JavaSourceFile> unchanged = new HashSet<JavaSourceFile>( + cachedSourceFiles); + unchanged.retainAll(newSourceFiles); + + Set<JavaSourceFile> changed = new HashSet<JavaSourceFile>(newSourceFiles); + changed.removeAll(unchanged); + + // First remove any stale units. + for (Iterator<CompilationUnit> it = unitMap.values().iterator(); it.hasNext();) { + CompilationUnit unit = it.next(); + SourceFileCompilationUnit sourceFileUnit = (SourceFileCompilationUnit) unit; + if (!unchanged.contains(sourceFileUnit.getSourceFile())) { + unit.setState(State.FRESH); + it.remove(); + } + } + + // Then add any new source files. + for (JavaSourceFile newSourceFile : changed) { + String typeName = newSourceFile.getTypeName(); + assert (!unitMap.containsKey(typeName)); + unitMap.put(typeName, new SourceFileCompilationUnit(newSourceFile)); + } + + // Record the update. + cachedSourceFiles = newSourceFiles; + } + + private void updateExposedUnits() { + exposedUnits = Collections.unmodifiableSet(new HashSet<CompilationUnit>( + unitMap.values())); + exposedClassFileMap = null; + } +}
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java b/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java new file mode 100644 index 0000000..e4e723a --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java
@@ -0,0 +1,258 @@ +/* + * Copyright 2008 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.dev.jdt.TypeRefVisitor; + +import org.eclipse.jdt.core.compiler.CategorizedProblem; +import org.eclipse.jdt.internal.compiler.ASTVisitor; +import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.lookup.BlockScope; +import org.eclipse.jdt.internal.compiler.lookup.ClassScope; +import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope; +import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; + +import java.util.Collections; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.Set; + +/** + * Encapsulates the state of a single active compilation unit in a particular + * module. State is accumulated throughout the life cycle of the containing + * module and may be invalidated at certain times and recomputed. + */ +public abstract class CompilationUnit { + + enum State { + COMPILED, CHECKED, ERROR, FRESH + } + + private class FindTypesInCud extends ASTVisitor { + Map<SourceTypeBinding, CompiledClass> map = new IdentityHashMap<SourceTypeBinding, CompiledClass>(); + + public Set<CompiledClass> getClasses() { + return new HashSet<CompiledClass>(map.values()); + } + + @Override + public boolean visit(TypeDeclaration typeDecl, BlockScope scope) { + CompiledClass enclosingClass = map.get(typeDecl.binding.enclosingType()); + assert (enclosingClass != null); + /* + * Weird case: if JDT determines that this local class is totally + * uninstantiable, it won't bother allocating a local name. + */ + if (typeDecl.binding.constantPoolName() != null) { + CompiledClass newClass = new CompiledClass(typeDecl, enclosingClass); + map.put(typeDecl.binding, newClass); + } + return true; + } + + @Override + public boolean visit(TypeDeclaration typeDecl, ClassScope scope) { + CompiledClass enclosingClass = map.get(typeDecl.binding.enclosingType()); + assert (enclosingClass != null); + CompiledClass newClass = new CompiledClass(typeDecl, enclosingClass); + map.put(typeDecl.binding, newClass); + return true; + } + + @Override + public boolean visit(TypeDeclaration typeDecl, CompilationUnitScope scope) { + assert (typeDecl.binding.enclosingType() == null); + CompiledClass newClass = new CompiledClass(typeDecl, null); + map.put(typeDecl.binding, newClass); + return true; + } + } + + private static Set<String> computeFileNameRefs(CompilationUnitDeclaration cud) { + final Set<String> result = new HashSet<String>(); + cud.traverse(new TypeRefVisitor() { + @Override + protected void onTypeRef(SourceTypeBinding referencedType, + CompilationUnitDeclaration unitOfReferrer) { + // Map the referenced type to the target compilation unit file. + result.add(String.valueOf(referencedType.getFileName())); + } + }, cud.scope); + return result; + } + + private CompilationUnitDeclaration cud; + private CategorizedProblem[] errors; + private Set<CompiledClass> exposedCompiledClasses; + private Set<String> fileNameRefs; + private State state = State.FRESH; + + /** + * Overridden to finalize; always returns object identity. + */ + @Override + public final boolean equals(Object obj) { + return super.equals(obj); + } + + /** + * Returns the user-relevant location of the source file. No programmatic + * assumptions should be made about the return value. + */ + public abstract String getDisplayLocation(); + + /** + * Returns the source code for this unit. + */ + public abstract String getSource(); + + /** + * Returns the fully-qualified name of the top level public type. + */ + public abstract String getTypeName(); + + /** + * Overridden to finalize; always returns identity hash code. + */ + @Override + public final int hashCode() { + return super.hashCode(); + } + + /** + * Returns <code>true</code> if this unit is compiled and valid. + */ + public boolean isCompiled() { + return state == State.COMPILED || state == State.CHECKED; + } + + /** + * Returns <code>true</code> if this unit was generated by a + * {@link com.google.gwt.core.ext.Generator}. + */ + public abstract boolean isGenerated(); + + /** + * Overridden to finalize; always returns {@link #getDisplayLocation()}. + */ + public final String toString() { + return getDisplayLocation(); + } + + /** + * Called when this unit no longer needs to keep an internal cache of its + * source. + */ + protected void dumpSource() { + } + + /** + * If compiled, returns all contained classes; otherwise returns + * <code>null</code>. + */ + Set<CompiledClass> getCompiledClasses() { + if (!isCompiled()) { + return null; + } + if (exposedCompiledClasses == null) { + FindTypesInCud typeFinder = new FindTypesInCud(); + cud.traverse(typeFinder, cud.scope); + Set<CompiledClass> compiledClasses = typeFinder.getClasses(); + exposedCompiledClasses = Collections.unmodifiableSet(compiledClasses); + } + return exposedCompiledClasses; + } + + CategorizedProblem[] getErrors() { + return errors; + } + + Set<String> getFileNameRefs() { + if (fileNameRefs == null) { + fileNameRefs = computeFileNameRefs(cud); + } + return fileNameRefs; + } + + /** + * If compiled, returns the JDT compilation unit declaration; otherwise + * <code>null</code>. + */ + CompilationUnitDeclaration getJdtCud() { + return cud; + } + + State getState() { + return state; + } + + /** + * Sets the compiled JDT AST for this unit. + */ + void setJdtCud(CompilationUnitDeclaration cud) { + this.cud = cud; + state = State.COMPILED; + } + + /** + * Changes the compilation unit's internal state. + */ + void setState(State newState) { + assert (newState != State.COMPILED); + if (state == newState) { + return; + } + state = newState; + + dumpSource(); + switch (newState) { + case CHECKED: + // Must cache before we destroy the cud. + assert (cud != null); + getFileNameRefs(); + for (CompiledClass compiledClass : getCompiledClasses()) { + compiledClass.checked(); + } + cud = null; + break; + + case ERROR: + this.errors = cud.compilationResult().getErrors(); + invalidate(); + break; + case FRESH: + this.errors = null; + invalidate(); + break; + } + } + + /** + * Removes all accumulated state associated with compilation. + */ + private void invalidate() { + cud = null; + fileNameRefs = null; + if (exposedCompiledClasses != null) { + for (CompiledClass compiledClass : exposedCompiledClasses) { + compiledClass.invalidate(); + } + exposedCompiledClasses = null; + } + } +}
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationUnitInvalidator.java b/dev/core/src/com/google/gwt/dev/javac/CompilationUnitInvalidator.java new file mode 100644 index 0000000..0a670ed --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/javac/CompilationUnitInvalidator.java
@@ -0,0 +1,155 @@ +/* + * Copyright 2008 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.TreeLogger; +import com.google.gwt.core.ext.TreeLogger.HelpInfo; +import com.google.gwt.dev.javac.CompilationUnit.State; +import com.google.gwt.dev.util.Util; + +import org.eclipse.jdt.core.compiler.CategorizedProblem; +import org.eclipse.jdt.internal.compiler.CompilationResult; +import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +/** + * Helper class to invalidate units in a set based on errors or references to + * other invalidate units. + * + * TODO: {@link ClassFileReader#hasStructuralChanges(byte[])} could help us + * optimize this process! + */ +public class CompilationUnitInvalidator { + + public static void invalidateUnitsWithErrors(TreeLogger logger, + Set<CompilationUnit> units) { + logger = logger.branch(TreeLogger.TRACE, "Removing units with errors"); + // Start by removing units with a known problem. + boolean anyRemoved = false; + for (CompilationUnit unit : units) { + CompilationUnitDeclaration cud = unit.getJdtCud(); + if (cud == null) { + continue; + } + CompilationResult result = cud.compilationResult(); + if (result.hasErrors()) { + anyRemoved = true; + + // TODO: defer this until later? + TreeLogger branch = logger.branch(TreeLogger.ERROR, "Errors in '" + + unit.getDisplayLocation() + "'", null); + + for (CategorizedProblem error : result.getErrors()) { + // Append 'Line #: msg' to the error message. + StringBuffer msgBuf = new StringBuffer(); + int line = error.getSourceLineNumber(); + if (line > 0) { + msgBuf.append("Line "); + msgBuf.append(line); + msgBuf.append(": "); + } + msgBuf.append(error.getMessage()); + + HelpInfo helpInfo = null; + if (error instanceof GWTProblem) { + GWTProblem gwtProblem = (GWTProblem) error; + helpInfo = gwtProblem.getHelpInfo(); + } + branch.log(TreeLogger.ERROR, msgBuf.toString(), null, helpInfo); + } + + Util.maybeDumpSource(branch, unit.getDisplayLocation(), + unit.getSource(), unit.getTypeName()); + + // TODO: hold onto errors? + unit.setState(State.ERROR); + } + } + + if (anyRemoved) { + // Then removing anything else that won't compile as a result. + invalidateUnitsWithInvalidRefs(logger, units); + } + } + + public static void invalidateUnitsWithInvalidRefs(TreeLogger logger, + Set<CompilationUnit> units) { + logger = logger.branch(TreeLogger.TRACE, "Removing invalidate units"); + + // Map all units by file name. + Map<String, CompilationUnit> unitsByFileName = new HashMap<String, CompilationUnit>(); + for (CompilationUnit unit : units) { + unitsByFileName.put(unit.getDisplayLocation(), unit); + } + // First, compute a map from all targets all referents. + Map<CompilationUnit, Set<CompilationUnit>> refTargetToReferents = new HashMap<CompilationUnit, Set<CompilationUnit>>(); + for (CompilationUnit referentUnit : units) { + if (referentUnit.isCompiled()) { + Set<String> fileNameRefs = referentUnit.getFileNameRefs(); + for (String fileNameRef : fileNameRefs) { + CompilationUnit targetUnit = unitsByFileName.get(fileNameRef); + if (targetUnit != null) { + Set<CompilationUnit> referents = refTargetToReferents.get(targetUnit); + if (referents == null) { + referents = new HashSet<CompilationUnit>(); + refTargetToReferents.put(targetUnit, referents); + } + // Add myself as a referent. + referents.add(referentUnit); + } + } + } + } + + // Now use the map to transitively blow away invalid units. + for (Entry<CompilationUnit, Set<CompilationUnit>> entry : refTargetToReferents.entrySet()) { + CompilationUnit maybeInvalidUnit = entry.getKey(); + if (!maybeInvalidUnit.isCompiled()) { + // Invalidate all dependent units. + Set<CompilationUnit> invalidReferentUnits = entry.getValue(); + TreeLogger branch = logger.branch(TreeLogger.TRACE, + "Compilation unit '" + maybeInvalidUnit + "' is invalid"); + State why = maybeInvalidUnit.getState(); + for (CompilationUnit invalidReferentUnit : invalidReferentUnits) { + if (invalidReferentUnit.isCompiled()) { + // Set it to the same state as the unit it depends on. + invalidReferentUnit.setState(why); + branch.log(TreeLogger.TRACE, "Removing dependent unit '" + + invalidReferentUnit + "'"); + } + } + } + } + } + + public static void validateCompilationUnits(Set<CompilationUnit> units, + Map<String, CompiledClass> compiledClasses) { + for (CompilationUnit unit : units) { + if (unit.getState() != State.CHECKED) { + CompilationUnitDeclaration jdtCud = unit.getJdtCud(); + JSORestrictionsChecker.check(jdtCud); + LongFromJSNIChecker.check(jdtCud); + BinaryTypeReferenceRestrictionsChecker.check(jdtCud, + compiledClasses.keySet()); + } + } + } +}
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java b/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java new file mode 100644 index 0000000..f3e1080 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java
@@ -0,0 +1,173 @@ +/* + * Copyright 2008 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.JRealClassType; +import com.google.gwt.dev.javac.impl.Shared; + +import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.internal.compiler.ClassFile; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; +import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; +import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; + +import java.util.Collections; +import java.util.List; + +/** + * Encapsulates the state of a single compiled class file. + */ +public final class CompiledClass { + + private static ClassFile getClassFile(TypeDeclaration typeDecl, + String binaryName) { + for (ClassFile tryClassFile : typeDecl.compilationResult().getClassFiles()) { + char[] tryBinaryName = CharOperation.concatWith( + tryClassFile.getCompoundName(), '/'); + if (binaryName.equals(String.valueOf(tryBinaryName))) { + return tryClassFile; + } + } + assert false; + return null; + } + + private static String getPackagePrefix(String packageName) { + return packageName.length() > 0 ? packageName + "." : ""; + } + + protected final String binaryName; + protected final byte[] bytes; + protected final CompiledClass enclosingClass; + protected final String location; + protected final String packageName; + protected final String sourceName; + + // The state below is transient. + private List<JsniMethod> jsniMethods; + private NameEnvironmentAnswer nameEnvironmentAnswer; + private JRealClassType realClassType; + // Can be killed after parent is CHECKED. + private TypeDeclaration typeDeclaration; + + CompiledClass(TypeDeclaration typeDeclaration, CompiledClass enclosingClass) { + SourceTypeBinding binding = typeDeclaration.binding; + this.typeDeclaration = typeDeclaration; + this.enclosingClass = enclosingClass; + this.binaryName = CharOperation.charToString(binding.constantPoolName()); + this.packageName = Shared.getPackageNameFromBinary(binaryName); + if (binding instanceof LocalTypeBinding) { + // The source name of a local type must be determined from binary. + String qualifiedName = binaryName.replace('/', '.'); + this.sourceName = qualifiedName.replace('$', '.'); + } else { + this.sourceName = getPackagePrefix(packageName) + + String.valueOf(binding.qualifiedSourceName()); + } + ClassFile classFile = getClassFile(typeDeclaration, binaryName); + this.bytes = classFile.getBytes(); + this.location = String.valueOf(classFile.fileName()); + } + + /** + * Returns the binary class name, e.g. {@code java/util/Map$Entry}. + */ + public String getBinaryName() { + return binaryName; + } + + /** + * Returns the bytes of the compiled class. + */ + public byte[] getBytes() { + return bytes; + } + + public CompiledClass getEnclosingClass() { + return enclosingClass; + } + + public List<JsniMethod> getJsniMethods() { + return jsniMethods; + } + + /** + * Returns the enclosing package, e.g. {@code java.util}. + */ + public String getPackageName() { + return packageName; + } + + /** + * Returns the qualified source name, e.g. {@code java.util.Map.Entry}. + */ + public String getSourceName() { + return sourceName; + } + + @Override + public String toString() { + return binaryName; + } + + /** + * All checking is done, free up internal state. + */ + void checked() { + this.typeDeclaration = null; + } + + NameEnvironmentAnswer getNameEnvironmentAnswer() { + if (nameEnvironmentAnswer == null) { + try { + ClassFileReader cfr = new ClassFileReader(bytes, location.toCharArray()); + nameEnvironmentAnswer = new NameEnvironmentAnswer(cfr, null); + } catch (ClassFormatException e) { + throw new RuntimeException("Unexpectedly unable to parse class file", e); + } + } + return nameEnvironmentAnswer; + } + + JRealClassType getRealClassType() { + return realClassType; + } + + TypeDeclaration getTypeDeclaration() { + return typeDeclaration; + } + + void invalidate() { + nameEnvironmentAnswer = null; + typeDeclaration = null; + jsniMethods = null; + if (realClassType != null) { + realClassType.invalidate(); + realClassType = null; + } + } + + void setJsniMethods(List<JsniMethod> jsniMethods) { + this.jsniMethods = Collections.unmodifiableList(jsniMethods); + } + + void setRealClassType(JRealClassType realClassType) { + this.realClassType = realClassType; + } +}
diff --git a/dev/core/src/com/google/gwt/dev/jdt/GWTProblem.java b/dev/core/src/com/google/gwt/dev/javac/GWTProblem.java similarity index 94% rename from dev/core/src/com/google/gwt/dev/jdt/GWTProblem.java rename to dev/core/src/com/google/gwt/dev/javac/GWTProblem.java index b61688e..645b6b7 100644 --- a/dev/core/src/com/google/gwt/dev/jdt/GWTProblem.java +++ b/dev/core/src/com/google/gwt/dev/javac/GWTProblem.java
@@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.google.gwt.dev.jdt; +package com.google.gwt.dev.javac; import com.google.gwt.core.ext.TreeLogger.HelpInfo; @@ -30,7 +30,7 @@ */ public class GWTProblem extends DefaultProblem { - static void recordInCud(ASTNode node, CompilationUnitDeclaration cud, + public static void recordInCud(ASTNode node, CompilationUnitDeclaration cud, String message, HelpInfo helpInfo) { CompilationResult compResult = cud.compilationResult(); int[] lineEnds = compResult.getLineSeparatorPositions();
diff --git a/dev/core/src/com/google/gwt/dev/jdt/JSORestrictionsChecker.java b/dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java similarity index 60% rename from dev/core/src/com/google/gwt/dev/jdt/JSORestrictionsChecker.java rename to dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java index 43e3df3..b6c7390 100644 --- a/dev/core/src/com/google/gwt/dev/jdt/JSORestrictionsChecker.java +++ b/dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java
@@ -13,12 +13,10 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.google.gwt.dev.jdt; +package com.google.gwt.dev.javac; -import com.google.gwt.dev.shell.JsValueGlue; import com.google.gwt.dev.util.InstalledHelpInfo; -import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; @@ -33,9 +31,10 @@ import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope; import org.eclipse.jdt.internal.compiler.lookup.MethodScope; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; -import org.eclipse.jdt.internal.compiler.lookup.Scope; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; +import java.util.Stack; + /** * Check a compilation unit for violations of * {@link com.google.gwt.core.client.JavaScriptObject JavaScriptObject} (JSO) @@ -58,100 +57,134 @@ * Any violations found are attached as errors on the * CompilationUnitDeclaration. */ -class JSORestrictionsChecker { +public class JSORestrictionsChecker { private class JSORestrictionsVisitor extends ASTVisitor implements ClassFileConstants { + private final Stack<Boolean> isJsoStack = new Stack<Boolean>(); + @Override public void endVisit(AllocationExpression exp, BlockScope scope) { - if (exp.type != null && isJSOSubclass(exp.type.resolveType(scope))) { + // Anywhere an allocation occurs is wrong. + if (exp.type != null && isJsoSubclass(exp.type.resolveType(scope))) { errorOn(exp, ERR_NEW_JSO); } } @Override public void endVisit(ConstructorDeclaration meth, ClassScope scope) { - if (isForJSOSubclass(scope)) { - if ((meth.arguments != null) && (meth.arguments.length > 0)) { - errorOn(meth, ERR_CONSTRUCTOR_WITH_PARAMETERS); - } - if ((meth.modifiers & AccProtected) == 0) { - errorOn(meth, ERR_NONPROTECTED_CONSTRUCTOR); - } - if (meth.statements != null && meth.statements.length > 0) { - errorOn(meth, ERR_NONEMPTY_CONSTRUCTOR); - } + if (!isJso()) { + return; + } + if ((meth.arguments != null) && (meth.arguments.length > 0)) { + errorOn(meth, ERR_CONSTRUCTOR_WITH_PARAMETERS); + } + if ((meth.modifiers & AccProtected) == 0) { + errorOn(meth, ERR_NONPROTECTED_CONSTRUCTOR); + } + if (meth.statements != null && meth.statements.length > 0) { + errorOn(meth, ERR_NONEMPTY_CONSTRUCTOR); } } @Override public void endVisit(FieldDeclaration field, MethodScope scope) { - if (isForJSOSubclass(scope)) { - if (!field.isStatic()) { - errorOn(field, ERR_INSTANCE_FIELD); - } + if (!isJso()) { + return; + } + if (!field.isStatic()) { + errorOn(field, ERR_INSTANCE_FIELD); } } @Override public void endVisit(MethodDeclaration meth, ClassScope scope) { - if (isForJSOSubclass(scope)) { - if ((meth.modifiers & (AccFinal | AccPrivate | AccStatic)) == 0) { - // The method's modifiers allow it to be overridden. Make - // one final check to see if the surrounding class is final. - if ((meth.scope == null) - || !meth.scope.enclosingSourceType().isFinal()) { - errorOn(meth, ERR_INSTANCE_METHOD_NONFINAL); - } + if (!isJso()) { + return; + } + if ((meth.modifiers & (AccFinal | AccPrivate | AccStatic)) == 0) { + // The method's modifiers allow it to be overridden. Make + // one final check to see if the surrounding class is final. + if ((meth.scope == null) || !meth.scope.enclosingSourceType().isFinal()) { + errorOn(meth, ERR_INSTANCE_METHOD_NONFINAL); } + } - // Should not have to check isStatic() here, but isOverriding() appears - // to be set for static methods. - if (!meth.isStatic() - && (meth.binding != null && meth.binding.isOverriding())) { - errorOn(meth, ERR_OVERRIDDEN_METHOD); - } + // Should not have to check isStatic() here, but isOverriding() appears + // to be set for static methods. + if (!meth.isStatic() + && (meth.binding != null && meth.binding.isOverriding())) { + errorOn(meth, ERR_OVERRIDDEN_METHOD); } } @Override public void endVisit(TypeDeclaration type, BlockScope scope) { - checkType(type); + popIsJso(); } @Override public void endVisit(TypeDeclaration type, ClassScope scope) { - checkType(type); + popIsJso(); } @Override public void endVisit(TypeDeclaration type, CompilationUnitScope scope) { - checkType(type); + popIsJso(); } - private void checkType(TypeDeclaration type) { - if (isJSOSubclass(type)) { - if (type.enclosingType != null && !type.binding.isStatic()) { - errorOn(type, ERR_IS_NONSTATIC_NESTED); - } + @Override + public boolean visit(TypeDeclaration type, BlockScope scope) { + pushIsJso(checkType(type)); + return true; + } - ReferenceBinding[] interfaces = type.binding.superInterfaces(); - if (interfaces != null) { - for (ReferenceBinding interf : interfaces) { - if (interf.methods() != null && interf.methods().length > 0) { - String intfName = String.copyValueOf(interf.shortReadableName()); - errorOn(type, errInterfaceWithMethods(intfName)); - } + @Override + public boolean visit(TypeDeclaration type, ClassScope scope) { + pushIsJso(checkType(type)); + return true; + } + + @Override + public boolean visit(TypeDeclaration type, CompilationUnitScope scope) { + pushIsJso(checkType(type)); + return true; + } + + private boolean checkType(TypeDeclaration type) { + if (!isJsoSubclass(type.binding)) { + return false; + } + if (type.enclosingType != null && !type.binding.isStatic()) { + errorOn(type, ERR_IS_NONSTATIC_NESTED); + } + + ReferenceBinding[] interfaces = type.binding.superInterfaces(); + if (interfaces != null) { + for (ReferenceBinding interf : interfaces) { + if (interf.methods() != null && interf.methods().length > 0) { + String intfName = String.copyValueOf(interf.shortReadableName()); + errorOn(type, errInterfaceWithMethods(intfName)); } } } + return true; + } + + private boolean isJso() { + return isJsoStack.peek(); + } + + private void popIsJso() { + isJsoStack.pop(); + } + + private void pushIsJso(boolean isJso) { + isJsoStack.push(isJso); } } - protected static final char[][] JSO_CLASS_CHARS = CharOperation.splitOn('.', - JsValueGlue.JSO_CLASS.toCharArray()); - static final String ERR_CONSTRUCTOR_WITH_PARAMETERS = "Constructors must not have parameters in subclasses of JavaScriptObject"; static final String ERR_INSTANCE_FIELD = "Instance fields cannot be used in subclasses of JavaScriptObject"; static final String ERR_INSTANCE_METHOD_NONFINAL = "Instance methods must be 'final' in non-final subclasses of JavaScriptObject"; @@ -160,6 +193,7 @@ static final String ERR_NONEMPTY_CONSTRUCTOR = "Constructors must be totally empty in subclasses of JavaScriptObject"; static final String ERR_NONPROTECTED_CONSTRUCTOR = "Constructors must be 'protected' in subclasses of JavaScriptObject"; static final String ERR_OVERRIDDEN_METHOD = "Methods cannot be overridden in JavaScriptObject subclasses"; + static final String JSO_CLASS = "com/google/gwt/core/client/JavaScriptObject"; /** * Checks an entire @@ -167,13 +201,7 @@ * */ public static void check(CompilationUnitDeclaration cud) { - TypeBinding jsoType = cud.scope.environment().getType(JSO_CLASS_CHARS); - if (jsoType == null) { - // JavaScriptObject not available; do nothing - return; - } - - JSORestrictionsChecker checker = new JSORestrictionsChecker(cud, jsoType); + JSORestrictionsChecker checker = new JSORestrictionsChecker(cud); checker.check(); } @@ -184,40 +212,30 @@ private final CompilationUnitDeclaration cud; - /** - * The type of the GWT JavaScriptObject class. Cannot be null. - */ - private final TypeBinding jsoType; - - private JSORestrictionsChecker(CompilationUnitDeclaration cud, - TypeBinding jsoType) { - assert jsoType != null; + private JSORestrictionsChecker(CompilationUnitDeclaration cud) { this.cud = cud; - this.jsoType = jsoType; } private void check() { cud.traverse(new JSORestrictionsVisitor(), cud.scope); } - private TypeBinding classType(Scope scope) { - return scope.classScope().referenceType().binding; - } - private void errorOn(ASTNode node, String error) { GWTProblem.recordInCud(node, cud, error, new InstalledHelpInfo( "jsoRestrictions.html")); } - private boolean isForJSOSubclass(Scope scope) { - return isJSOSubclass(classType(scope)); - } - - private boolean isJSOSubclass(TypeBinding typeBinding) { - return typeBinding.isCompatibleWith(jsoType) && typeBinding != jsoType; - } - - private boolean isJSOSubclass(TypeDeclaration typeDecl) { - return isJSOSubclass(typeDecl.binding); + private boolean isJsoSubclass(TypeBinding typeBinding) { + if (!(typeBinding instanceof ReferenceBinding)) { + return false; + } + ReferenceBinding binding = (ReferenceBinding) typeBinding; + while (binding.superclass() != null) { + if (JSO_CLASS.equals(String.valueOf(binding.superclass().constantPoolName()))) { + return true; + } + binding = binding.superclass(); + } + return false; } }
diff --git a/dev/core/src/com/google/gwt/dev/javac/JavaSourceFile.java b/dev/core/src/com/google/gwt/dev/javac/JavaSourceFile.java new file mode 100644 index 0000000..f73c2da --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/javac/JavaSourceFile.java
@@ -0,0 +1,74 @@ +/* + * Copyright 2008 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; + +/** + * Provides information about a single Java source file. + */ +public abstract class JavaSourceFile { + + /** + * Overridden to finalize; always returns object identity. + */ + @Override + public final boolean equals(Object obj) { + return super.equals(obj); + } + + /** + * Returns the user-relevant location of the source file. No programmatic + * assumptions should be made about the return value. + */ + public abstract String getLocation(); + + /** + * Returns the name of the package. + */ + public abstract String getPackageName(); + + /** + * Returns the unqualified name of the top level public type. + */ + public abstract String getShortName(); + + /** + * Returns the fully-qualified name of the top level public type. + */ + public abstract String getTypeName(); + + /** + * Overridden to finalize; always returns identity hash code. + */ + @Override + public final int hashCode() { + return super.hashCode(); + } + + /** + * Returns the Java code contained in this source file. May return + * <code>null</code> if this {@link JavaSourceFile} has been invalidated by + * its containing {@link JavaSourceOracle}. This method may be expensive as + * the implementor is generally not required to cache the results. + */ + public abstract String readSource(); + + /** + * Overridden to finalize; always returns {@link #getLocation()}. + */ + public final String toString() { + return getLocation(); + } +}
diff --git a/dev/core/src/com/google/gwt/dev/javac/JavaSourceOracle.java b/dev/core/src/com/google/gwt/dev/javac/JavaSourceOracle.java new file mode 100644 index 0000000..12d5841 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/javac/JavaSourceOracle.java
@@ -0,0 +1,69 @@ +/* + * Copyright 2006 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 java.util.Map; +import java.util.Set; + +/** + * An unmodifiable view of a module's Java source tree. + * + * <p> + * The identity of the returned sets and maps will change exactly when the + * underlying module is refreshed. + * </p> + * + * <p> + * Even when the identity of a returned set changes, the identity of any + * contained {@link JavaSourceFile} values is guaranteed to differ from a + * previous result exactly when that particular source file becomes invalid. + * </p> + * + * <p> + * A source file could become invalid for various reasons, including: + * <ul> + * <li>the underlying file was deleted or modified</li> + * <li>another file with the same logical name superceded it on the classpath</li> + * <li>the underlying module changed to exclude this file or supercede it with + * another file</li> + * </ul> + * </p> + * + * <p> + * After a refresh, a client can reliably detect changes by checking which of + * its cached source files is still contained in the new result of + * {@link #getSourceFiles()}. + * </p> + */ +public interface JavaSourceOracle { + + /** + * Returns an unmodifiable set of fully-qualified class names with constant + * lookup time. + */ + Set<String> getClassNames(); + + /** + * Returns an unmodifiable set of unique source files with constant lookup + * time. + */ + Set<JavaSourceFile> getSourceFiles(); + + /** + * Returns an unmodifiable map of fully-qualified class name to source file. + */ + Map<String, JavaSourceFile> getSourceMap(); +}
diff --git a/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java b/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java new file mode 100644 index 0000000..831b46a --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java
@@ -0,0 +1,279 @@ +/* + * Copyright 2008 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.dev.javac.impl.Shared; +import com.google.gwt.dev.util.PerfLogger; +import com.google.gwt.util.tools.Utility; + +import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.internal.compiler.CompilationResult; +import org.eclipse.jdt.internal.compiler.Compiler; +import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies; +import org.eclipse.jdt.internal.compiler.ICompilerRequestor; +import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; +import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; +import org.eclipse.jdt.internal.compiler.env.INameEnvironment; +import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; +import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; +import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +/** + * Manages the process of compiling {@link CompilationUnit}s. + */ +public class JdtCompiler { + + /** + * Adapts a {@link CompilationUnit} for a JDT compile. + */ + public static class CompilationUnitAdapter implements ICompilationUnit { + + private final CompilationUnit unit; + + public CompilationUnitAdapter(CompilationUnit unit) { + this.unit = unit; + } + + public char[] getContents() { + return unit.getSource().toString().toCharArray(); + } + + public char[] getFileName() { + return unit.getDisplayLocation().toCharArray(); + } + + public char[] getMainTypeName() { + return Shared.getShortName(unit.getTypeName()).toCharArray(); + } + + public char[][] getPackageName() { + String packageName = Shared.getPackageName(unit.getTypeName()); + return CharOperation.splitOn('.', packageName.toCharArray()); + } + + public CompilationUnit getUnit() { + return unit; + } + + @Override + public String toString() { + return unit.toString(); + } + } + private class CompilerImpl extends Compiler { + + public CompilerImpl() { + super(new INameEnvironmentImpl(), + DefaultErrorHandlingPolicies.proceedWithAllProblems(), + getCompilerOptions(), new ICompilerRequestorImpl(), + new DefaultProblemFactory(Locale.getDefault())); + } + + @Override + public void process(CompilationUnitDeclaration cud, int i) { + // TODO: not always generate bytecode eagerly? + super.process(cud, i); + ICompilationUnit icu = cud.compilationResult().compilationUnit; + CompilationUnitAdapter adapter = (CompilationUnitAdapter) icu; + adapter.getUnit().setJdtCud(cud); + } + } + + /** + * Hook point to accept results. + */ + private class ICompilerRequestorImpl implements ICompilerRequestor { + public void acceptResult(CompilationResult result) { + } + } + + /** + * How JDT receives files from the environment. + */ + private class INameEnvironmentImpl implements INameEnvironment { + public void cleanup() { + } + + public NameEnvironmentAnswer findType(char[] type, char[][] pkg) { + return findType(CharOperation.arrayConcat(pkg, type)); + } + + public NameEnvironmentAnswer findType(char[][] compoundTypeName) { + char[] binaryNameChars = CharOperation.concatWith(compoundTypeName, '.'); + String binaryName = String.valueOf(binaryNameChars); + CompiledClass compiledClass = binaryTypes.get(binaryName); + if (compiledClass != null) { + return compiledClass.getNameEnvironmentAnswer(); + } + if (isPackage(binaryName)) { + return null; + } + try { + // Check for binary-only annotations. + Class.forName(binaryName, false, getClassLoader()); + String resourcePath = binaryName.replace('.', '/') + ".class"; + URL resource = getClassLoader().getResource(resourcePath); + InputStream openStream = resource.openStream(); + try { + ClassFileReader cfr = ClassFileReader.read(openStream, + resource.toExternalForm(), true); + return new NameEnvironmentAnswer(cfr, null); + } finally { + Utility.close(openStream); + } + } catch (NoClassDefFoundError e) { + } catch (ClassNotFoundException e) { + } catch (ClassFormatException e) { + } catch (IOException e) { + } + return null; + } + + public boolean isPackage(char[][] parentPkg, char[] pkg) { + final char[] pathChars = CharOperation.concatWith(parentPkg, pkg, '.'); + String packageName = String.valueOf(pathChars); + return isPackage(packageName); + } + + private ClassLoader getClassLoader() { + return Thread.currentThread().getContextClassLoader(); + } + + private boolean isPackage(String packageName) { + // Include class loader check for binary-only annotations. + if (packages.contains(packageName)) { + return true; + } + if (notPackages.contains(packageName)) { + return false; + } + String resourceName = packageName.replace('.', '/') + '/'; + if (getClassLoader().getResource(resourceName) != null) { + addPackages(packageName); + return true; + } else { + notPackages.add(packageName); + return false; + } + } + } + + /** + * Compiles the given set of units. The units will be internally modified to + * reflect the results of compilation. + */ + public static void compile(Collection<CompilationUnit> units) { + PerfLogger.start("JdtCompiler.compile"); + new JdtCompiler().doCompile(units); + PerfLogger.end(); + } + + private static CompilerOptions getCompilerOptions() { + Map<String, String> settings = new HashMap<String, String>(); + settings.put(CompilerOptions.OPTION_LineNumberAttribute, + CompilerOptions.GENERATE); + settings.put(CompilerOptions.OPTION_SourceFileAttribute, + CompilerOptions.GENERATE); + /* + * Tricks like "boolean stopHere = true;" depend on this setting to work in + * hosted mode. In web mode, our compiler should optimize them out once we + * do real data flow. + */ + settings.put(CompilerOptions.OPTION_PreserveUnusedLocal, + CompilerOptions.PRESERVE); + settings.put(CompilerOptions.OPTION_ReportDeprecation, + CompilerOptions.IGNORE); + settings.put(CompilerOptions.OPTION_LocalVariableAttribute, + CompilerOptions.GENERATE); + settings.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_1_5); + settings.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_1_5); + settings.put(CompilerOptions.OPTION_TargetPlatform, + CompilerOptions.VERSION_1_5); + + // This is needed by TypeOracleBuilder to parse metadata. + settings.put(CompilerOptions.OPTION_DocCommentSupport, + CompilerOptions.ENABLED); + return new CompilerOptions(settings); + } + + private final List<CompilationUnit> activeUnits = new ArrayList<CompilationUnit>(); + + /** + * Maps dotted binary names to compiled classes. + */ + private final Map<String, CompiledClass> binaryTypes = new HashMap<String, CompiledClass>(); + + private final CompilerImpl compiler = new CompilerImpl(); + + private final Set<String> notPackages = new HashSet<String>(); + + private final Set<String> packages = new HashSet<String>(); + + /** + * Not externally instantiable. + */ + private JdtCompiler() { + } + + private void addPackages(String packageName) { + while (true) { + packages.add(String.valueOf(packageName)); + int pos = packageName.lastIndexOf('.'); + if (pos > 0) { + packageName = packageName.substring(0, pos); + } else { + packages.add(""); + break; + } + } + } + + private void doCompile(Collection<CompilationUnit> units) { + List<ICompilationUnit> icus = new ArrayList<ICompilationUnit>(); + for (CompilationUnit unit : units) { + String packageName = Shared.getPackageName(unit.getTypeName()); + addPackages(packageName); + Set<CompiledClass> compiledClasses = unit.getCompiledClasses(); + if (compiledClasses == null) { + icus.add(new CompilationUnitAdapter(unit)); + activeUnits.add(unit); + } else { + for (CompiledClass compiledClass : compiledClasses) { + binaryTypes.put(compiledClass.getBinaryName().replace('/', '.'), + compiledClass); + } + } + } + if (!icus.isEmpty()) { + compiler.compile(icus.toArray(new ICompilationUnit[icus.size()])); + } + } + +}
diff --git a/dev/core/src/com/google/gwt/dev/javac/JsniCollector.java b/dev/core/src/com/google/gwt/dev/javac/JsniCollector.java new file mode 100644 index 0000000..6acae2c --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/javac/JsniCollector.java
@@ -0,0 +1,300 @@ +/* + * Copyright 2008 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.TreeLogger; +import com.google.gwt.dev.javac.CompilationUnit.State; +import com.google.gwt.dev.js.JsParser; +import com.google.gwt.dev.js.JsParserException; +import com.google.gwt.dev.js.JsParserException.SourceDetail; +import com.google.gwt.dev.js.ast.JsExprStmt; +import com.google.gwt.dev.js.ast.JsFunction; +import com.google.gwt.dev.js.ast.JsProgram; +import com.google.gwt.dev.js.ast.JsStatement; +import com.google.gwt.dev.util.Empty; +import com.google.gwt.dev.util.Jsni; + +import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.Argument; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.util.Util; + +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Adapts compilation units containing JSNI-accessible code by rewriting the + * source. + */ +public class JsniCollector { + + /** + * Represents a logical interval of text. + */ + public static class Interval { + public final int end; + public final int start; + + public Interval(int start, int end) { + this.start = start; + this.end = end; + } + } + + private static final class JsniMethodImpl extends JsniMethod { + private JsFunction func; + private final int line; + private final String location; + private final String name; + private final String[] paramNames; + private final String source; + private final JsProgram program; + + private JsniMethodImpl(String name, String source, String[] paramNames, + int line, String location, JsProgram program) { + this.name = name; + this.source = source; + this.paramNames = paramNames; + this.line = line; + this.location = location; + this.program = program; + } + + @Override + public JsFunction function(TreeLogger logger) { + if (func == null) { + func = parseAsAnonymousFunction(logger, program, source, paramNames, + location, line); + } + return func; + } + + public int line() { + return line; + } + + public String location() { + return location; + } + + public String name() { + return name; + } + + public String[] paramNames() { + return paramNames; + } + + public String source() { + return source; + } + + @Override + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("function "); + sb.append(name); + sb.append('('); + boolean first = true; + for (String paramName : paramNames) { + if (first) { + first = false; + } else { + sb.append(", "); + } + sb.append(paramName); + } + sb.append("} {\n"); + sb.append(source); + sb.append("}\n"); + return sb.toString(); + } + } + + public static void collectJsniMethods(TreeLogger logger, + Set<CompilationUnit> units, JsProgram program) { + for (CompilationUnit unit : units) { + if (unit.getState() == State.COMPILED) { + String loc = unit.getDisplayLocation(); + String source = unit.getSource(); + for (CompiledClass compiledClass : unit.getCompiledClasses()) { + assert compiledClass.getJsniMethods() == null; + collectJsniMethods(logger, loc, source, compiledClass, program); + } + } + } + } + + /** + * TODO: log real errors, replacing GenerateJavaScriptAST? + */ + private static void collectJsniMethods(TreeLogger logger, String loc, + String source, CompiledClass compiledClass, JsProgram program) { + TypeDeclaration typeDecl = compiledClass.getTypeDeclaration(); + int[] lineEnds = typeDecl.compilationResult.getLineSeparatorPositions(); + List<JsniMethod> jsniMethods = new ArrayList<JsniMethod>(); + String enclosingType = compiledClass.getBinaryName().replace('/', '.'); + AbstractMethodDeclaration[] methods = typeDecl.methods; + if (methods != null) { + for (AbstractMethodDeclaration method : methods) { + if (!method.isNative()) { + continue; + } + Interval interval = findJsniSource(source, method); + if (interval == null) { + String msg = "No JavaScript body found for native method '" + method + + "' in type '" + compiledClass.getSourceName() + "'"; + logger.log(TreeLogger.ERROR, msg, null); + continue; + } + + String js = source.substring(interval.start, interval.end); + int startLine = Util.getLineNumber(interval.start, lineEnds, 0, + lineEnds.length - 1); + String jsniSignature = getJsniSignature(enclosingType, method); + String[] paramNames = getParamNames(method); + + jsniMethods.add(new JsniMethodImpl(jsniSignature, js, paramNames, + startLine, loc, program)); + } + } + compiledClass.setJsniMethods(jsniMethods); + } + + private static Interval findJsniSource(String source, + AbstractMethodDeclaration method) { + assert (method.isNative()); + int bodyStart = method.bodyStart; + int bodyEnd = method.bodyEnd; + String js = source.substring(bodyStart, bodyEnd + 1); + + int jsniStart = js.indexOf(Jsni.JSNI_BLOCK_START); + if (jsniStart == -1) { + return null; + } + + int jsniEnd = js.indexOf(Jsni.JSNI_BLOCK_END, jsniStart); + if (jsniEnd == -1) { + // Suspicious, but maybe this is just a weird comment, so let it slide. + // + return null; + } + + int srcStart = bodyStart + jsniStart + Jsni.JSNI_BLOCK_START.length(); + int srcEnd = bodyStart + jsniEnd; + return new Interval(srcStart, srcEnd); + } + + /** + * Gets a unique name for this method and its signature (this is used to + * determine whether one method overrides another). + */ + private static String getJsniSignature(String enclosingType, + AbstractMethodDeclaration method) { + return '@' + enclosingType + "::" + getMemberSignature(method); + } + + /** + * Gets a unique name for this method and its signature (this is used to + * determine whether one method overrides another). + */ + private static String getMemberSignature(AbstractMethodDeclaration method) { + String name = String.valueOf(method.selector); + StringBuilder sb = new StringBuilder(); + sb.append(name); + sb.append("("); + if (method.arguments != null) { + for (Argument param : method.arguments) { + sb.append(param.binding.type.signature()); + } + } + sb.append(")"); + return sb.toString(); + } + + private static String[] getParamNames(AbstractMethodDeclaration method) { + if (method.arguments != null) { + String[] paramNames = new String[method.arguments.length]; + for (int i = 0; i < paramNames.length; ++i) { + paramNames[i] = String.valueOf(method.arguments[i].name); + } + return paramNames; + } + return Empty.STRINGS; + } + + /** + * TODO: rip out problem reporting code from BuildTypeMap and attach errors to + * the compilation units. + */ + private static JsFunction parseAsAnonymousFunction(TreeLogger logger, + JsProgram program, String js, String[] paramNames, String location, + int startLine) { + + // Wrap the code in an anonymous function and parse it. + StringReader r; + { + StringBuilder sb = new StringBuilder(); + sb.append("function ("); + boolean first = true; + for (String paramName : paramNames) { + if (first) { + first = false; + } else { + sb.append(','); + } + sb.append(paramName); + } + sb.append(") {"); + sb.append(js); + sb.append('}'); + r = new StringReader(sb.toString()); + } + + try { + List<JsStatement> stmts = new JsParser().parse(program.getScope(), r, + startLine); + + return (JsFunction) ((JsExprStmt) stmts.get(0)).getExpression(); + } catch (IOException e) { + // Should never happen. + throw new RuntimeException(e); + } catch (JsParserException e) { + SourceDetail dtl = e.getSourceDetail(); + if (dtl != null) { + StringBuilder sb = new StringBuilder(); + sb.append(location); + sb.append("("); + sb.append(dtl.getLine()); + sb.append(", "); + sb.append(dtl.getLineOffset()); + sb.append("): "); + sb.append(e.getMessage()); + logger.log(TreeLogger.ERROR, sb.toString(), e); + return null; + } else { + logger.log(TreeLogger.ERROR, "Error parsing JSNI source", e); + return null; + } + } + } + + private JsniCollector() { + } +}
diff --git a/dev/core/src/com/google/gwt/dev/javac/JsniMethod.java b/dev/core/src/com/google/gwt/dev/javac/JsniMethod.java new file mode 100644 index 0000000..d45a460 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/javac/JsniMethod.java
@@ -0,0 +1,55 @@ +/* + * Copyright 2008 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.TreeLogger; +import com.google.gwt.dev.js.ast.JsFunction; + +/** + * Represents a single JsniMethod in a compiled class file. + */ +public abstract class JsniMethod { + /** + * If non-null, an anonymous function containing the parameters and body of + * this JSNI method. + */ + public abstract JsFunction function(TreeLogger logger); + + /** + * Starting line number of the method. + */ + public abstract int line(); + + /** + * Location of the containing compilation unit. + */ + public abstract String location(); + + /** + * The mangled method name (a jsni signature). + */ + public abstract String name(); + + /** + * The parameter names. + */ + public abstract String[] paramNames(); + + /** + * The script body. + */ + public abstract String source(); +}
diff --git a/dev/core/src/com/google/gwt/dev/jdt/LongFromJSNIChecker.java b/dev/core/src/com/google/gwt/dev/javac/LongFromJSNIChecker.java similarity index 98% rename from dev/core/src/com/google/gwt/dev/jdt/LongFromJSNIChecker.java rename to dev/core/src/com/google/gwt/dev/javac/LongFromJSNIChecker.java index e404c8c..fa49fd6 100644 --- a/dev/core/src/com/google/gwt/dev/jdt/LongFromJSNIChecker.java +++ b/dev/core/src/com/google/gwt/dev/javac/LongFromJSNIChecker.java
@@ -13,9 +13,10 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.google.gwt.dev.jdt; +package com.google.gwt.dev.javac; import com.google.gwt.core.client.UnsafeNativeLong; +import com.google.gwt.dev.jdt.FindJsniRefVisitor; import com.google.gwt.dev.util.InstalledHelpInfo; import com.google.gwt.dev.util.JsniRef;
diff --git a/dev/core/src/com/google/gwt/dev/jdt/TypeOracleBuilder.java b/dev/core/src/com/google/gwt/dev/javac/TypeOracleMediator.java similarity index 69% rename from dev/core/src/com/google/gwt/dev/jdt/TypeOracleBuilder.java rename to dev/core/src/com/google/gwt/dev/javac/TypeOracleMediator.java index 0af0fac..a58513c 100644 --- a/dev/core/src/com/google/gwt/dev/jdt/TypeOracleBuilder.java +++ b/dev/core/src/com/google/gwt/dev/javac/TypeOracleMediator.java
@@ -13,11 +13,10 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.google.gwt.dev.jdt; +package com.google.gwt.dev.javac; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider; import com.google.gwt.core.ext.typeinfo.HasMetaData; import com.google.gwt.core.ext.typeinfo.HasTypeParameters; import com.google.gwt.core.ext.typeinfo.JAbstractMethod; @@ -38,17 +37,14 @@ import com.google.gwt.core.ext.typeinfo.JRealClassType; import com.google.gwt.core.ext.typeinfo.JType; import com.google.gwt.core.ext.typeinfo.JTypeParameter; +import com.google.gwt.core.ext.typeinfo.NotFoundException; import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.core.ext.typeinfo.JWildcardType.BoundType; -import com.google.gwt.dev.jdt.CacheManager.Mapper; +import com.google.gwt.dev.javac.impl.Shared; import com.google.gwt.dev.util.Empty; import com.google.gwt.dev.util.PerfLogger; -import com.google.gwt.dev.util.Util; import org.eclipse.jdt.core.compiler.CharOperation; -import org.eclipse.jdt.core.compiler.IProblem; -import org.eclipse.jdt.internal.compiler.ASTVisitor; -import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration; import org.eclipse.jdt.internal.compiler.ast.Annotation; @@ -57,7 +53,6 @@ import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer; import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess; import org.eclipse.jdt.internal.compiler.ast.Clinit; -import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.Initializer; @@ -69,13 +64,10 @@ import org.eclipse.jdt.internal.compiler.ast.TypeParameter; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.ast.Wildcard; -import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; import org.eclipse.jdt.internal.compiler.impl.Constant; import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding; import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding; -import org.eclipse.jdt.internal.compiler.lookup.BlockScope; -import org.eclipse.jdt.internal.compiler.lookup.ClassScope; import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope; import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; @@ -89,40 +81,26 @@ import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding; import java.io.BufferedReader; -import java.io.CharArrayReader; -import java.io.File; import java.io.IOException; +import java.io.StringReader; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Array; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; +import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.TreeMap; import java.util.regex.Pattern; /** - * Builds a {@link com.google.gwt.core.ext.typeinfo.TypeOracle} from a set of - * compilation units. - * <p> - * For example, - * - * <pre> - * TypeOracleBuilder b = new TypeOracleBuilder(); - * b.addCompilationUnit(unit1); - * b.addCompilationUnit(unit2); - * b.addCompilationUnit(unit3); - * b.excludePackage("example.pkg"); - * TypeOracle oracle = b.build(logger); - * JClassType[] allTypes = oracle.getTypes(); - * </pre> + * Builds or rebuilds a {@link com.google.gwt.core.ext.typeinfo.TypeOracle} from + * a set of compilation units. */ -public class TypeOracleBuilder { +public class TypeOracleMediator { + private static final JClassType[] NO_JCLASSES = new JClassType[0]; private static final Pattern PATTERN_WHITESPACE = Pattern.compile("\\s"); @@ -160,22 +138,23 @@ return classType.getQualifiedSourceName(); } - static boolean parseMetaDataTags(char[] unitSource, HasMetaData hasMetaData, + static boolean parseMetaDataTags(String unitSource, HasMetaData hasMetaData, Javadoc javadoc) { int start = javadoc.sourceStart; int end = javadoc.sourceEnd; - char[] comment = CharOperation.subarray(unitSource, start, end + 1); - if (comment == null) { - comment = new char[0]; + if (start < 0 || end > unitSource.length() || start > end) { + // Invalid. + return false; } - BufferedReader reader = new BufferedReader(new CharArrayReader(comment)); + String comment = unitSource.substring(start, end + 1); + BufferedReader reader = new BufferedReader(new StringReader( + comment.toString())); String activeTag = null; final List<String> tagValues = new ArrayList<String>(); try { - String line = reader.readLine(); boolean firstLine = true; - while (line != null) { + for (String line = reader.readLine(); line != null; line = reader.readLine()) { if (firstLine) { firstLine = false; int commentStart = line.indexOf("/**"); @@ -186,26 +165,27 @@ line = line.substring(commentStart + 3); } + if (activeTag == null && line.indexOf('@') < 0) { + continue; + } + String[] tokens = PATTERN_WHITESPACE.split(line); boolean canIgnoreStar = true; for (int i = 0; i < tokens.length; i++) { String token = tokens[i]; // Check for the end. - // if (token.endsWith("*/")) { token = token.substring(0, token.length() - 2); } // Check for an ignored leading star. - // if (canIgnoreStar && token.startsWith("*")) { token = token.substring(1); canIgnoreStar = false; } // Decide what to do with whatever is left. - // if (token.length() > 0) { canIgnoreStar = false; if (token.startsWith("@")) { @@ -227,8 +207,6 @@ } } } - - line = reader.readLine(); } } catch (IOException e) { return false; @@ -304,14 +282,6 @@ return retentionPolicy; } - private static boolean isAnnotation(TypeDeclaration typeDecl) { - if (TypeDeclaration.kind(typeDecl.modifiers) == TypeDeclaration.ANNOTATION_TYPE_DECL) { - return true; - } else { - return false; - } - } - /** * Returns <code>true</code> if this name is the special package-info type * name. @@ -326,6 +296,11 @@ private static boolean maybeGeneric(TypeDeclaration typeDecl, JClassType enclosingType) { + if (typeDecl.typeParameters != null) { + // Definitely generic since it has type parameters. + return true; + } + if (enclosingType != null && enclosingType.isGenericType() != null) { if (!typeDecl.binding.isStatic()) { /* @@ -351,11 +326,6 @@ } } - if (typeDecl.typeParameters != null) { - // Definitely generic since it has type parameters. - return true; - } - return false; } @@ -364,309 +334,72 @@ return new HashMap<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation>(); } - private static void removeInfectedUnits(final TreeLogger logger, - final Map<String, CompilationUnitDeclaration> changedCudsByFileName, - final Map<String, CompilationUnitDeclaration> unchangedCudsByFileName) { + private final Map<String, JRealClassType> binaryMapper = new HashMap<String, JRealClassType>(); + private final Map<SourceTypeBinding, JRealClassType> sourceMapper = new IdentityHashMap<SourceTypeBinding, JRealClassType>(); + private final Map<TypeVariableBinding, JTypeParameter> tvMapper = new IdentityHashMap<TypeVariableBinding, JTypeParameter>(); + private final TypeOracle typeOracle = new TypeOracle(); + private final Set<JRealClassType> unresolvedTypes = new HashSet<JRealClassType>(); - final Set<String> pendingRemovals = new HashSet<String>(); - TypeRefVisitor trv = new TypeRefVisitor() { - @Override - protected void onTypeRef(SourceTypeBinding referencedType, - CompilationUnitDeclaration unitOfReferrer) { - // If the referenced type belongs to a compilation unit that is - // not in the list of valid units, then the unit in which it - // is referenced must also be removed. - // - String referencedFn = String.valueOf(referencedType.getFileName()); - if (!unchangedCudsByFileName.containsKey(referencedFn) - && !changedCudsByFileName.containsKey(referencedFn)) { - // This is a referenced to a bad or non-existent unit. - // So, remove the referrer's unit if it hasn't been already. - // - String referrerFn = String.valueOf(unitOfReferrer.getFileName()); - if (changedCudsByFileName.containsKey(referrerFn) - && !pendingRemovals.contains(referrerFn)) { - TreeLogger branch = logger.branch(TreeLogger.TRACE, - "Cascaded removal of compilation unit '" + referrerFn + "'", - null); - final String badTypeName = CharOperation.toString(referencedType.compoundName); - branch.branch(TreeLogger.TRACE, - "Due to reference to unavailable type: " + badTypeName, null); - pendingRemovals.add(referrerFn); - } - } - } - }; - - do { - // Perform any pending removals. - // - for (Iterator<String> iter = pendingRemovals.iterator(); iter.hasNext();) { - String fnToRemove = iter.next(); - Object removed = changedCudsByFileName.remove(fnToRemove); - assert (removed != null); - } - - // Start fresh for this iteration. - // - pendingRemovals.clear(); - - // Find references to type in units that aren't valid. - // - for (Iterator<CompilationUnitDeclaration> iter = changedCudsByFileName.values().iterator(); iter.hasNext();) { - CompilationUnitDeclaration cud = iter.next(); - cud.traverse(trv, cud.scope); - } - } while (!pendingRemovals.isEmpty()); + public TypeOracle getTypeOracle() { + return typeOracle; } - private static void removeUnitsWithErrors(TreeLogger logger, - Map<String, CompilationUnitDeclaration> changedCudsByFileName, - Map<String, CompilationUnitDeclaration> unchangedCudsByFileName) { - // Start by removing units with a known problem. - // - boolean anyRemoved = false; - for (Iterator<CompilationUnitDeclaration> iter = changedCudsByFileName.values().iterator(); iter.hasNext();) { - CompilationUnitDeclaration cud = iter.next(); - CompilationResult result = cud.compilationResult; - IProblem[] errors = result.getErrors(); - if (errors != null && errors.length > 0) { - anyRemoved = true; - iter.remove(); - - String fileName = CharOperation.charToString(cud.getFileName()); - char[] source = cud.compilationResult.compilationUnit.getContents(); - Util.maybeDumpSource(logger, fileName, source, - String.valueOf(cud.getMainTypeName())); - logger.log(TreeLogger.TRACE, "Removing problematic compilation unit '" - + fileName + "'", null); - } - } - - if (anyRemoved) { - // Then removing anything else that won't compile as a result. - // - removeInfectedUnits(logger, changedCudsByFileName, - unchangedCudsByFileName); - } - } - - private final CacheManager cacheManager; - - /** - * Constructs a default instance, with a default cacheManager. This is not to - * be used in Hosted Mode, as caching will then not work. - */ - public TypeOracleBuilder() { - cacheManager = new CacheManager(); - } - - /** - * Constructs an instance from the supplied cacheManager, using the - * <code>TypeOracle</code> contained therein. This is to be used in Hosted - * Mode, so that caching will work, assuming the cacheManager has a cache - * directory. - */ - public TypeOracleBuilder(CacheManager cacheManager) { - this.cacheManager = cacheManager; - } - - /** - * Constructs an instance from the supplied typeOracle, with a cacheManager - * using the same typeOracle. This is not to be used in Hosted Mode, as - * caching will then not work. - */ - public TypeOracleBuilder(TypeOracle typeOracle) { - cacheManager = new CacheManager(typeOracle); - } - - /** - * Includes the specified logical compilation unit into the set of units this - * builder will parse and analyze. If a previous compilation unit was - * specified in the same location, it will be replaced if it is older. - */ - public void addCompilationUnit(CompilationUnitProvider cup) + public void refresh(TreeLogger logger, Set<CompilationUnit> units) throws UnableToCompleteException { - cacheManager.addCompilationUnit(cup); - } + PerfLogger.start("TypeOracleMediator.refresh"); + typeOracle.removeInvalidatedTypes(); + binaryMapper.clear(); + sourceMapper.clear(); + tvMapper.clear(); + unresolvedTypes.clear(); - public TypeOracle build(final TreeLogger logger) - throws UnableToCompleteException { - PerfLogger.start("TypeOracleBuilder.build"); - - Set<CompilationUnitProvider> addedCups = cacheManager.getAddedCups(); - TypeOracle oracle = cacheManager.getTypeOracle(); - // Make a copy that we can sort. - // - for (Iterator<CompilationUnitProvider> iter = addedCups.iterator(); iter.hasNext();) { - CompilationUnitProvider cup = iter.next(); - String location = cup.getLocation(); - // TODO: Delegate to cup in order to check for deletification. - if (!((location.indexOf("http://") != -1) || (location.indexOf("ftp://") != -1))) { - location = Util.findFileName(location); - if (!(new File(location).exists() || cup.isTransient())) { - iter.remove(); - logger.log( - TreeLogger.TRACE, - "The file " - + location - + " was removed by the user. All types therein are now unavailable.", - null); + // Perform a shallow pass to establish identity for new and old types. + PerfLogger.start("TypeOracleMediator.refresh (shallow)"); + for (CompilationUnit unit : units) { + if (!unit.isCompiled()) { + continue; + } + Set<CompiledClass> compiledClasses = unit.getCompiledClasses(); + for (CompiledClass compiledClass : compiledClasses) { + JRealClassType type = compiledClass.getRealClassType(); + if (type == null) { + type = createType(compiledClass); } + binaryMapper.put(compiledClass.getBinaryName(), type); } } - CompilationUnitProvider[] cups = Util.toArray( - CompilationUnitProvider.class, addedCups); - Arrays.sort(cups, CompilationUnitProvider.LOCATION_COMPARATOR); - - // Make sure we can find the java.lang.Object compilation unit. - // - boolean foundJavaLangPackage = oracle.findPackage("java.lang") != null; - - // Adapt to JDT idioms. - // - ICompilationUnit[] units = new ICompilationUnit[cups.length]; - for (int i = 0; i < cups.length; i++) { - if (!foundJavaLangPackage && cups[i].getPackageName().equals("java.lang")) { - foundJavaLangPackage = true; - } - units[i] = cacheManager.findUnitForCup(cups[i]); - } - - // Error if no java.lang. - if (!foundJavaLangPackage) { - Util.logMissingTypeErrorWithHints(logger, "java.lang.Object"); - throw new UnableToCompleteException(); - } - - PerfLogger.start("TypeOracleBuilder.build (compile)"); - /* - * The assumption is that getting the changed CUDs will include anything - * that transitively depends on the changed CUDs. - */ - CompilationUnitDeclaration[] cuds = cacheManager.getAstCompiler().getChangedCompilationUnitDeclarations( - logger, units); PerfLogger.end(); - // Build a list that makes it easy to remove problems. - // - final Map<String, CompilationUnitDeclaration> unchangedCudsByFileName = new TreeMap<String, CompilationUnitDeclaration>(); - unchangedCudsByFileName.putAll(cacheManager.getCudsByFileName()); - final Map<String, CompilationUnitDeclaration> changedCudsByFileName = new TreeMap<String, CompilationUnitDeclaration>(); - for (int i = 0; i < cuds.length; i++) { - CompilationUnitDeclaration cud = cuds[i]; - String fileName = String.valueOf(cud.getFileName()); - changedCudsByFileName.put(fileName, cud); - /* - * The CacheManager's cuds by file name may include the changed CUDs from - * a previous refresh. So we remove them here to ensure that our sets do - * not overlap. - */ - unchangedCudsByFileName.remove(fileName); - } - - /* - * Note that the CacheManager can easily end up with bad CUDs if the newly - * compiled files have errors. However, this does not impact the TypeOracle. - */ - cacheManager.getCudsByFileName().putAll(changedCudsByFileName); - - /* - * At this point changedCudsByFileName contains only the CUDs which needed - * to be recompiled and unchangedCudsByFileName contains only the CUDs which - * were not recompiled. Now we can scan changedCudsByFileName an remove any - * CUDs which have errors and any CUDs that are infected by those errors. - */ - removeUnitsWithErrors(logger, changedCudsByFileName, - unchangedCudsByFileName); - - // Perform a shallow pass to establish identity for new types. - // - final CacheManager.Mapper identityMapper = cacheManager.getIdentityMapper(); - for (Iterator<CompilationUnitDeclaration> iter = changedCudsByFileName.values().iterator(); iter.hasNext();) { - CompilationUnitDeclaration cud = iter.next(); - - cud.traverse(new ASTVisitor() { - @Override - public boolean visit(TypeDeclaration typeDecl, BlockScope scope) { - JClassType enclosingType = identityMapper.get(typeDecl.binding.enclosingType()); - processType(typeDecl, enclosingType, true); - return true; - } - - @Override - public boolean visit(TypeDeclaration typeDecl, ClassScope scope) { - JClassType enclosingType = identityMapper.get(typeDecl.binding.enclosingType()); - processType(typeDecl, enclosingType, false); - return true; - } - - @Override - public boolean visit(TypeDeclaration typeDecl, - CompilationUnitScope scope) { - processType(typeDecl, null, false); - return true; - } - - }, cud.scope); - } - - // Perform a deep pass to resolve all types in terms of our types. - // - for (Iterator<CompilationUnitDeclaration> iter = changedCudsByFileName.values().iterator(); iter.hasNext();) { - CompilationUnitDeclaration cud = iter.next(); - String loc = String.valueOf(cud.getFileName()); - String processing = "Processing types in compilation unit: " + loc; - final TreeLogger cudLogger = logger.branch(TreeLogger.SPAM, processing, - null); - final char[] source = cud.compilationResult.compilationUnit.getContents(); - - cud.traverse(new ASTVisitor() { - @Override - public boolean visit(TypeDeclaration typeDecl, BlockScope scope) { - if (!resolveTypeDeclaration(cudLogger, source, typeDecl)) { - String name = String.valueOf(typeDecl.binding.readableName()); - String msg = "Unexpectedly unable to fully resolve type " + name; - logger.log(TreeLogger.WARN, msg, null); + // Perform a deep pass to resolve all new types in terms of our types. + PerfLogger.start("TypeOracleMediator.refresh (deep)"); + for (CompilationUnit unit : units) { + if (!unit.isCompiled()) { + continue; + } + TreeLogger cudLogger = logger.branch(TreeLogger.SPAM, + "Processing types in compilation unit: " + unit.getDisplayLocation()); + Set<CompiledClass> compiledClasses = unit.getCompiledClasses(); + for (CompiledClass compiledClass : compiledClasses) { + if (unresolvedTypes.contains(compiledClass.getRealClassType())) { + TypeDeclaration typeDeclaration = compiledClass.getTypeDeclaration(); + if (!resolveTypeDeclaration(cudLogger, unit.getSource(), typeDeclaration)) { + logger.log(TreeLogger.WARN, + "Unexpectedly unable to fully resolve type " + + compiledClass.getSourceName()); } - return true; } - - @Override - public boolean visit(TypeDeclaration typeDecl, ClassScope scope) { - if (!resolveTypeDeclaration(cudLogger, source, typeDecl)) { - String name = String.valueOf(typeDecl.binding.readableName()); - String msg = "Unexpectedly unable to fully resolve type " + name; - logger.log(TreeLogger.WARN, msg, null); - } - return true; - } - - @Override - public boolean visit(TypeDeclaration typeDecl, - CompilationUnitScope scope) { - if (!resolveTypeDeclaration(cudLogger, source, typeDecl)) { - String name = String.valueOf(typeDecl.binding.readableName()); - String msg = "Unexpectedly unable to fully resolve type " + name; - logger.log(TreeLogger.WARN, msg, null); - } - return true; - } - }, cud.scope); + } } - Util.invokeInaccessableMethod(TypeOracle.class, "refresh", - new Class[] {TreeLogger.class}, oracle, new Object[] {logger}); - PerfLogger.end(); - return oracle; - } + try { + typeOracle.refresh(logger); + } catch (NotFoundException e) { + // TODO + e.printStackTrace(); + } - /** - * Used for testing purposes only. - */ - final CacheManager getCacheManager() { - return cacheManager; + PerfLogger.end(); } private Object createAnnotationInstance(TreeLogger logger, @@ -710,6 +443,86 @@ return AnnotationProxyFactory.create(clazz, identifierToValue); } + private JRealClassType createType(CompiledClass compiledClass) { + JRealClassType realClassType = compiledClass.getRealClassType(); + if (realClassType == null) { + JRealClassType enclosingType = null; + CompiledClass enclosingClass = compiledClass.getEnclosingClass(); + if (enclosingClass != null) { + enclosingType = enclosingClass.getRealClassType(); + if (enclosingType == null) { + enclosingType = createType(enclosingClass); + } + } + realClassType = createType(compiledClass, enclosingType); + if (realClassType != null) { + unresolvedTypes.add(realClassType); + sourceMapper.put(compiledClass.getTypeDeclaration().binding, + realClassType); + compiledClass.setRealClassType(realClassType); + } + } + return realClassType; + } + + /** + * Maps a TypeDeclaration into a JRealClassType. If the TypeDeclaration has + * TypeParameters (i.e, it is a generic type or method), then the + * TypeParameters are mapped into JTypeParameters. + */ + private JRealClassType createType(CompiledClass compiledClass, + JRealClassType enclosingType) { + TypeDeclaration typeDecl = compiledClass.getTypeDeclaration(); + SourceTypeBinding binding = typeDecl.binding; + assert (binding.constantPoolName() != null); + + String qname = compiledClass.getSourceName(); + String className = Shared.getShortName(qname); + String jpkgName = compiledClass.getPackageName(); + JPackage pkg = typeOracle.getOrCreatePackage(jpkgName); + boolean isLocalType = binding instanceof LocalTypeBinding; + boolean isIntf = TypeDeclaration.kind(typeDecl.modifiers) == TypeDeclaration.INTERFACE_DECL; + boolean isAnnotation = TypeDeclaration.kind(typeDecl.modifiers) == TypeDeclaration.ANNOTATION_TYPE_DECL; + + JRealClassType resultType; + if (isAnnotation) { + resultType = new JAnnotationType(typeOracle, pkg, enclosingType, + isLocalType, className, isIntf); + } else if (maybeGeneric(typeDecl, enclosingType)) { + // Go through and create declarations for each of the type parameters on + // the generic class or method + JTypeParameter[] jtypeParameters = declareTypeParameters(typeDecl.typeParameters); + + JGenericType jgenericType = new JGenericType(typeOracle, pkg, + enclosingType, isLocalType, className, isIntf, jtypeParameters); + + resultType = jgenericType; + } else if (binding.isEnum()) { + resultType = new JEnumType(typeOracle, pkg, enclosingType, isLocalType, + className, isIntf); + } else { + resultType = new JRealClassType(typeOracle, pkg, enclosingType, + isLocalType, className, isIntf); + } + + /* + * Declare type parameters for all methods; we must do this during the first + * pass. + */ + if (typeDecl.methods != null) { + for (AbstractMethodDeclaration method : typeDecl.methods) { + declareTypeParameters(method.typeParameters()); + } + } + + /* + * Add modifiers since these are needed for + * TypeOracle.getParameterizedType's error checking code. + */ + resultType.addModifierBits(Shared.bindingToModifierBits(binding)); + return resultType; + } + private JClassType[] createTypeParameterBounds(TreeLogger logger, TypeVariableBinding tvBinding) { TypeBinding firstBound = tvBinding.firstBound; @@ -782,12 +595,10 @@ } JTypeParameter[] jtypeParamArray = new JTypeParameter[typeParameters.length]; - Mapper identityMapper = cacheManager.getIdentityMapper(); - - for (int i = 0; i < typeParameters.length; i++) { + for (int i = 0; i < typeParameters.length; ++i) { TypeParameter typeParam = typeParameters[i]; jtypeParamArray[i] = new JTypeParameter(String.valueOf(typeParam.name), i); - identityMapper.put(typeParam.binding, jtypeParamArray[i]); + tvMapper.put(typeParam.binding, jtypeParamArray[i]); } return jtypeParamArray; @@ -935,7 +746,7 @@ String className = String.valueOf(resolvedType.constantPoolName()); className = className.replace('/', '.'); return Class.forName(className, false, - TypeOracleBuilder.class.getClassLoader()); + Thread.currentThread().getContextClassLoader()); } catch (ClassNotFoundException e) { logger.log(TreeLogger.ERROR, "", e); return null; @@ -969,17 +780,6 @@ } } - private CompilationUnitProvider getCup(TypeDeclaration typeDecl) { - ICompilationUnit icu = typeDecl.compilationResult.compilationUnit; - ICompilationUnitAdapter icua = (ICompilationUnitAdapter) icu; - return icua.getCompilationUnitProvider(); - } - - private String getPackage(TypeDeclaration typeDecl) { - final char[][] pkgParts = typeDecl.compilationResult.compilationUnit.getPackageName(); - return String.valueOf(CharOperation.concatWith(pkgParts, '.')); - } - /** * Returns the qualified name of the binding, excluding any type parameter * information. @@ -999,101 +799,9 @@ } qualifiedName = qualifiedName.replace('$', '.'); - return qualifiedName; } - private String getSimpleName(TypeDeclaration typeDecl) { - return String.valueOf(typeDecl.name); - } - - private boolean isInterface(TypeDeclaration typeDecl) { - if (TypeDeclaration.kind(typeDecl.modifiers) == TypeDeclaration.INTERFACE_DECL) { - return true; - } else { - return false; - } - } - - /** - * Maps a TypeDeclaration into a JClassType. If the TypeDeclaration has - * TypeParameters (i.e, it is a generic type or method), then the - * TypeParameters are mapped into JTypeParameters by - * {@link TypeOracleBuilder#declareTypeParameters(org.eclipse.jdt.internal.compiler.ast.TypeParameter[])} - */ - private void processType(TypeDeclaration typeDecl, JClassType jenclosingType, - boolean isLocalType) { - TypeOracle oracle = cacheManager.getTypeOracle(); - - // Create our version of the type structure unless it already exists in the - // type oracle. - // - SourceTypeBinding binding = typeDecl.binding; - if (binding.constantPoolName() == null) { - /* - * Weird case: if JDT determines that this local class is totally - * uninstantiable, it won't bother allocating a local name. - */ - return; - } - - String qname = getQualifiedName(binding); - String className; - if (binding instanceof LocalTypeBinding) { - className = qname.substring(qname.lastIndexOf('.') + 1); - } else { - className = getSimpleName(typeDecl); - } - - if (oracle.findType(qname) != null) { - return; - } - - String jpkgName = getPackage(typeDecl); - JPackage pkg = oracle.getOrCreatePackage(jpkgName); - final boolean jclassIsIntf = isInterface(typeDecl); - boolean jclassIsAnnonation = isAnnotation(typeDecl); - CompilationUnitProvider cup = getCup(typeDecl); - - int declStart = typeDecl.declarationSourceStart; - int declEnd = typeDecl.declarationSourceEnd; - int bodyStart = typeDecl.bodyStart; - int bodyEnd = typeDecl.bodyEnd; - - JRealClassType jrealClassType; - if (jclassIsAnnonation) { - jrealClassType = new JAnnotationType(oracle, cup, pkg, jenclosingType, - isLocalType, className, declStart, declEnd, bodyStart, bodyEnd, - jclassIsIntf); - } else if (maybeGeneric(typeDecl, jenclosingType)) { - // Go through and create declarations for each of the type parameters on - // the generic class or method - JTypeParameter[] jtypeParameters = declareTypeParameters(typeDecl.typeParameters); - - JGenericType jgenericType = new JGenericType(oracle, cup, pkg, - jenclosingType, isLocalType, className, declStart, declEnd, - bodyStart, bodyEnd, jclassIsIntf, jtypeParameters); - - jrealClassType = jgenericType; - } else if (binding.isEnum()) { - jrealClassType = new JEnumType(oracle, cup, pkg, jenclosingType, - isLocalType, className, declStart, declEnd, bodyStart, bodyEnd, - jclassIsIntf); - } else { - jrealClassType = new JRealClassType(oracle, cup, pkg, jenclosingType, - isLocalType, className, declStart, declEnd, bodyStart, bodyEnd, - jclassIsIntf); - } - - /* - * Add modifiers since these are needed for - * TypeOracle.getParameterizedType's error checking code. - */ - jrealClassType.addModifierBits(Shared.bindingToModifierBits(binding)); - - cacheManager.setTypeForBinding(binding, jrealClassType); - } - private boolean resolveAnnotation( TreeLogger logger, Annotation jannotation, @@ -1146,7 +854,6 @@ } genericElement.getTypeParameters()[ordinal].setBounds(jbounds); - return true; } @@ -1163,7 +870,7 @@ return true; } - private boolean resolveField(TreeLogger logger, char[] unitSource, + private boolean resolveField(TreeLogger logger, String unitSource, JClassType enclosingType, FieldDeclaration jfield) { if (jfield instanceof Initializer) { @@ -1213,7 +920,7 @@ return true; } - private boolean resolveFields(TreeLogger logger, char[] unitSource, + private boolean resolveFields(TreeLogger logger, String unitSource, JClassType type, FieldDeclaration[] jfields) { if (jfields != null) { for (int i = 0; i < jfields.length; i++) { @@ -1226,7 +933,7 @@ return true; } - private boolean resolveMethod(TreeLogger logger, char[] unitSource, + private boolean resolveMethod(TreeLogger logger, String unitSource, JClassType enclosingType, AbstractMethodDeclaration jmethod) { if (jmethod instanceof Clinit) { @@ -1235,10 +942,6 @@ return true; } - int declStart = jmethod.declarationSourceStart; - int declEnd = jmethod.declarationSourceEnd; - int bodyStart = jmethod.bodyStart; - int bodyEnd = jmethod.bodyEnd; String name = getMethodName(enclosingType, jmethod); // Try to resolve annotations, ignore any that fail. @@ -1250,11 +953,11 @@ // Declare the type parameters. We will pass them into the constructors for // JConstructor/JMethod/JAnnotatedMethod. Then, we'll do a second pass to // resolve the bounds on each JTypeParameter object. - JTypeParameter[] jtypeParameters = declareTypeParameters(jmethod.typeParameters()); + JTypeParameter[] jtypeParameters = resolveTypeParameters(jmethod.typeParameters()); if (jmethod.isConstructor()) { - method = new JConstructor(enclosingType, name, declStart, declEnd, - bodyStart, bodyEnd, declaredAnnotations, jtypeParameters); + method = new JConstructor(enclosingType, name, declaredAnnotations, + jtypeParameters); // Do a second pass to resolve the bounds on each JTypeParameter. if (!resolveBoundsForTypeParameters(logger, method, jmethod.typeParameters())) { @@ -1269,11 +972,11 @@ annotationMethod.returnType.resolvedType, annotationMethod.defaultValue); } - method = new JAnnotationMethod(enclosingType, name, declStart, declEnd, - bodyStart, bodyEnd, defaultValue, declaredAnnotations); + method = new JAnnotationMethod(enclosingType, name, defaultValue, + declaredAnnotations); } else { - method = new JMethod(enclosingType, name, declStart, declEnd, - bodyStart, bodyEnd, declaredAnnotations, jtypeParameters); + method = new JMethod(enclosingType, name, declaredAnnotations, + jtypeParameters); } // Do a second pass to resolve the bounds on each JTypeParameter. @@ -1328,7 +1031,7 @@ return true; } - private boolean resolveMethods(TreeLogger logger, char[] unitSource, + private boolean resolveMethods(TreeLogger logger, String unitSource, JClassType type, AbstractMethodDeclaration[] jmethods) { if (jmethods != null) { for (int i = 0; i < jmethods.length; i++) { @@ -1344,9 +1047,8 @@ private boolean resolvePackage(TreeLogger logger, TypeDeclaration jclass) { SourceTypeBinding binding = jclass.binding; - TypeOracle oracle = cacheManager.getTypeOracle(); String packageName = String.valueOf(binding.fPackage.readableName()); - JPackage pkg = oracle.getOrCreatePackage(packageName); + JPackage pkg = typeOracle.getOrCreatePackage(packageName); assert (pkg != null); CompilationUnitScope cus = (CompilationUnitScope) jclass.scope.parent; @@ -1358,7 +1060,6 @@ declaredAnnotations); pkg.addAnnotations(declaredAnnotations); - return true; } @@ -1426,9 +1127,7 @@ } private JType resolveType(TreeLogger logger, TypeBinding binding) { - TypeOracle oracle = cacheManager.getTypeOracle(); // Check for primitives. - // if (binding instanceof BaseTypeBinding) { switch (binding.id) { case TypeIds.T_boolean: @@ -1470,10 +1169,10 @@ // oracle we're assimilating into. // String typeName = getQualifiedName(referenceBinding); - JType resolvedType = oracle.findType(typeName); + JType resolvedType = typeOracle.findType(typeName); if (resolvedType == null) { // Otherwise, it should be something we've mapped during this build. - resolvedType = cacheManager.getTypeForBinding(referenceBinding); + resolvedType = sourceMapper.get(referenceBinding); } if (resolvedType != null) { @@ -1486,6 +1185,15 @@ } } + if (binding instanceof BinaryTypeBinding) { + // Try a binary lookup. + String binaryName = String.valueOf(binding.constantPoolName()); + JRealClassType realClassType = binaryMapper.get(binaryName); + if (realClassType != null) { + return realClassType; + } + } + // Check for an array. // if (binding instanceof ArrayBinding) { @@ -1501,7 +1209,7 @@ // By using the oracle to intern, we guarantee correct identity // mapping of lazily-created array types. // - resolvedType = oracle.getArrayType(resolvedType); + resolvedType = typeOracle.getArrayType(resolvedType); } return resolvedType; } else { @@ -1552,7 +1260,7 @@ if (!failed) { if (resolveType.isGenericType() != null) { - return oracle.getParameterizedType(resolveType.isGenericType(), + return typeOracle.getParameterizedType(resolveType.isGenericType(), enclosingType, typeArguments); } else { /* @@ -1570,7 +1278,7 @@ if (binding instanceof TypeVariableBinding) { TypeVariableBinding tvBinding = (TypeVariableBinding) binding; - JTypeParameter typeParameter = (JTypeParameter) cacheManager.getTypeForBinding(tvBinding); + JTypeParameter typeParameter = tvMapper.get(tvBinding); if (typeParameter != null) { return typeParameter; } @@ -1610,7 +1318,7 @@ } if (boundType != null) { - return oracle.getWildcardType(boundType, typeBound); + return typeOracle.getWildcardType(boundType, typeBound); } // Fall-through to failure @@ -1630,16 +1338,10 @@ return null; } - private boolean resolveTypeDeclaration(TreeLogger logger, char[] unitSource, + private boolean resolveTypeDeclaration(TreeLogger logger, String unitSource, TypeDeclaration clazz) { SourceTypeBinding binding = clazz.binding; - if (binding.constantPoolName() == null) { - /* - * Weird case: if JDT determines that this local class is totally - * uninstantiable, it won't bother allocating a local name. - */ - return true; - } + assert (binding.constantPoolName() != null); String qname = String.valueOf(binding.qualifiedSourceName()); logger = logger.branch(TreeLogger.SPAM, "Found type '" + qname + "'", null); @@ -1682,11 +1384,11 @@ // if (jtype.isInterface() == null) { ReferenceBinding superclassRef = binding.superclass; + assert superclassRef != null + || "java.lang.Object".equals(jtype.getQualifiedSourceName()); if (superclassRef != null) { JClassType jsuperClass = (JClassType) resolveType(logger, superclassRef); - if (jsuperClass == null) { - return false; - } + assert jsuperClass != null; jtype.setSuperclass(jsuperClass); } } @@ -1738,4 +1440,29 @@ return true; } + + /** + * Declares TypeParameters declared on a JGenericType or a JAbstractMethod by + * mapping the TypeParameters into JTypeParameters. <p/> This mapping has to + * be done on the first pass through the AST in order to handle declarations + * of the form: <<C exends GenericClass<T>, T extends SimpleClass> <p/> JDT + * already knows that GenericClass<T> is a parameterized type with a type + * argument of <T extends SimpleClass>. Therefore, in order to resolve + * GenericClass<T>, we must have knowledge of <T extends SimpleClass>. <p/> + * By calling this method on the first pass through the AST, a JTypeParameter + * for <T extends SimpleClass> will be created. + */ + private JTypeParameter[] resolveTypeParameters(TypeParameter[] typeParameters) { + if (typeParameters == null || typeParameters.length == 0) { + return null; + } + + JTypeParameter[] jtypeParamArray = new JTypeParameter[typeParameters.length]; + for (int i = 0; i < typeParameters.length; ++i) { + jtypeParamArray[i] = tvMapper.get(typeParameters[i].binding); + assert jtypeParamArray[i] != null; + } + + return jtypeParamArray; + } }
diff --git a/dev/core/src/com/google/gwt/dev/javac/impl/FileCompilationUnit.java b/dev/core/src/com/google/gwt/dev/javac/impl/FileCompilationUnit.java new file mode 100644 index 0000000..3194633 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/javac/impl/FileCompilationUnit.java
@@ -0,0 +1,61 @@ +/* + * Copyright 2008 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.impl; + +import com.google.gwt.dev.javac.CompilationUnit; +import com.google.gwt.dev.util.Util; + +import java.io.File; + +/** + * A compilation unit based on a file. + */ +public final class FileCompilationUnit extends CompilationUnit { + private final File file; + private final String typeName; + + public FileCompilationUnit(File file, String packageName) { + this.file = file; + String fileName = file.getName(); + assert fileName.endsWith(".java"); + fileName = fileName.substring(0, fileName.length() - 5); + if (packageName.length() == 0) { + this.typeName = fileName; + } else { + this.typeName = packageName + '.' + fileName; + } + } + + @Override + public String getDisplayLocation() { + return file.getAbsolutePath(); + } + + @Override + public String getSource() { + return Util.readFileAsString(file); + } + + @Override + public String getTypeName() { + return typeName; + } + + @Override + public boolean isGenerated() { + return false; + } +} \ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/javac/impl/JavaSourceOracleImpl.java b/dev/core/src/com/google/gwt/dev/javac/impl/JavaSourceOracleImpl.java new file mode 100644 index 0000000..820c214 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/javac/impl/JavaSourceOracleImpl.java
@@ -0,0 +1,191 @@ +/* + * Copyright 2008 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.impl; + +import com.google.gwt.dev.javac.JavaSourceFile; +import com.google.gwt.dev.javac.JavaSourceOracle; +import com.google.gwt.dev.resource.Resource; +import com.google.gwt.dev.resource.ResourceOracle; +import com.google.gwt.dev.util.Util; + +import java.io.InputStream; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * Implements {@link JavaSourceOracle} on top of a {@link ResourceOracle}. + */ +public class JavaSourceOracleImpl implements JavaSourceOracle { + + private static class JavaSourceFileImpl extends JavaSourceFile { + + private final String location; + private final String name; + private final String packageName; + private Resource resource; + private final String shortName; + + public JavaSourceFileImpl(Resource resource) { + this.resource = resource; + location = resource.getLocation(); + String path = resource.getPath(); + assert (path.endsWith(".java")); + path = path.substring(0, path.lastIndexOf('.')); + name = path.replace('/', '.'); + int pos = name.lastIndexOf('.'); + if (pos < 0) { + shortName = name; + packageName = ""; + } else { + shortName = name.substring(pos + 1); + packageName = name.substring(0, pos); + } + } + + @Override + public String getLocation() { + return location; + } + + @Override + public String getPackageName() { + return packageName; + } + + @Override + public String getShortName() { + return shortName; + } + + @Override + public String getTypeName() { + return name; + } + + @Override + public String readSource() { + if (resource != null) { + InputStream contents = resource.openContents(); + return Util.readStreamAsString(contents); + } + return null; + } + + Resource getResource() { + return resource; + } + + void invalidate() { + resource = null; + } + } + + /** + * The last resource set returned by my oracle. + */ + private Set<Resource> cachedResources = Collections.emptySet(); + + /** + * An unmodifiable set of exposedClassNames to return to a client. + */ + private Set<String> exposedClassNames = Collections.emptySet(); + + /** + * An unmodifiable set of exposedSourceFiles to return to a client. + */ + private Set<JavaSourceFile> exposedSourceFiles = Collections.emptySet(); + + /** + * An unmodifiable source map to return to a client. + */ + private Map<String, JavaSourceFile> exposedSourceMap = Collections.emptyMap(); + + /** + * My resource oracle. + */ + private final ResourceOracle oracle; + + /** + * My internal set of source files. + */ + private final Set<JavaSourceFileImpl> sourceFiles = new HashSet<JavaSourceFileImpl>(); + + public JavaSourceOracleImpl(ResourceOracle oracle) { + this.oracle = oracle; + } + + public Set<String> getClassNames() { + refresh(); + return exposedClassNames; + } + + public Set<JavaSourceFile> getSourceFiles() { + refresh(); + return exposedSourceFiles; + } + + public Map<String, JavaSourceFile> getSourceMap() { + refresh(); + return exposedSourceMap; + } + + private void refresh() { + Set<Resource> newResources = oracle.getResources(); + if (newResources == cachedResources) { + // We're up to date. + return; + } + + // Divide resources into changed and unchanged. + Set<Resource> unchanged = new HashSet<Resource>(cachedResources); + unchanged.retainAll(newResources); + + Set<Resource> changed = new HashSet<Resource>(newResources); + changed.removeAll(unchanged); + + // First remove any stale source files. + for (Iterator<JavaSourceFileImpl> it = sourceFiles.iterator(); it.hasNext();) { + JavaSourceFileImpl sourceFile = it.next(); + if (!unchanged.contains(sourceFile.getResource())) { + sourceFile.invalidate(); + it.remove(); + } + } + + // Then add any new source files. + for (Resource newResource : changed) { + sourceFiles.add(new JavaSourceFileImpl(newResource)); + } + + // Finally rebuild the unmodifiable views. + Map<String, JavaSourceFile> sourceMap = new HashMap<String, JavaSourceFile>(); + for (JavaSourceFileImpl sourceFile : sourceFiles) { + sourceMap.put(sourceFile.getTypeName(), sourceFile); + } + exposedSourceMap = Collections.unmodifiableMap(sourceMap); + exposedClassNames = Collections.unmodifiableSet(sourceMap.keySet()); + HashSet<JavaSourceFile> sourceFilesConstantLookup = new HashSet<JavaSourceFile>( + sourceMap.values()); + exposedSourceFiles = Collections.unmodifiableSet(sourceFilesConstantLookup); + + // Record the update. + cachedResources = newResources; + } +}
diff --git a/dev/core/src/com/google/gwt/dev/javac/impl/Shared.java b/dev/core/src/com/google/gwt/dev/javac/impl/Shared.java new file mode 100644 index 0000000..3cf1613 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/javac/impl/Shared.java
@@ -0,0 +1,188 @@ +/* + * Copyright 2008 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.impl; + +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.core.ext.typeinfo.JConstructor; +import com.google.gwt.core.ext.typeinfo.JField; +import com.google.gwt.core.ext.typeinfo.JMethod; +import com.google.gwt.core.ext.typeinfo.JPackage; +import com.google.gwt.core.ext.typeinfo.JParameter; +import com.google.gwt.core.ext.typeinfo.JType; +import com.google.gwt.dev.util.Util; +import com.google.gwt.util.tools.Utility; + +import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; +import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * A grab bag of utility functions useful for dealing with java files. + */ +public class Shared { + + public static final int MOD_ABSTRACT = 0x00000001; + public static final int MOD_FINAL = 0x00000002; + public static final int MOD_NATIVE = 0x00000004; + public static final int MOD_PRIVATE = 0x00000008; + public static final int MOD_PROTECTED = 0x00000010; + public static final int MOD_PUBLIC = 0x00000020; + public static final int MOD_STATIC = 0x00000040; + public static final int MOD_TRANSIENT = 0x00000080; + public static final int MOD_VOLATILE = 0x00000100; + public static final JClassType[] NO_JCLASSES = new JClassType[0]; + public static final JConstructor[] NO_JCTORS = new JConstructor[0]; + public static final JField[] NO_JFIELDS = new JField[0]; + public static final JMethod[] NO_JMETHODS = new JMethod[0]; + public static final JPackage[] NO_JPACKAGES = new JPackage[0]; + public static final JParameter[] NO_JPARAMS = new JParameter[0]; + public static final JType[] NO_JTYPES = new JType[0]; + public static final String[][] NO_STRING_ARR_ARR = new String[0][]; + public static final String[] NO_STRINGS = new String[0]; + + public static int bindingToModifierBits(FieldBinding binding) { + int bits = 0; + bits |= (binding.isPublic() ? MOD_PUBLIC : 0); + bits |= (binding.isPrivate() ? MOD_PRIVATE : 0); + bits |= (binding.isProtected() ? MOD_PROTECTED : 0); + bits |= (binding.isStatic() ? MOD_STATIC : 0); + bits |= (binding.isTransient() ? MOD_TRANSIENT : 0); + bits |= (binding.isFinal() ? MOD_FINAL : 0); + bits |= (binding.isVolatile() ? MOD_VOLATILE : 0); + return bits; + } + + public static int bindingToModifierBits(MethodBinding binding) { + int bits = 0; + bits |= (binding.isPublic() ? MOD_PUBLIC : 0); + bits |= (binding.isPrivate() ? MOD_PRIVATE : 0); + bits |= (binding.isProtected() ? MOD_PROTECTED : 0); + bits |= (binding.isStatic() ? MOD_STATIC : 0); + bits |= (binding.isFinal() ? MOD_FINAL : 0); + bits |= (binding.isNative() ? MOD_NATIVE : 0); + bits |= (binding.isAbstract() ? MOD_ABSTRACT : 0); + return bits; + } + + public static int bindingToModifierBits(ReferenceBinding binding) { + int bits = 0; + bits |= (binding.isPublic() ? MOD_PUBLIC : 0); + bits |= (binding.isPrivate() ? MOD_PRIVATE : 0); + bits |= (binding.isProtected() ? MOD_PROTECTED : 0); + bits |= (binding.isStatic() ? MOD_STATIC : 0); + bits |= (binding.isFinal() ? MOD_FINAL : 0); + bits |= (binding.isAbstract() ? MOD_ABSTRACT : 0); + return bits; + } + + public static String getPackageName(String qualifiedTypeName) { + int pos = qualifiedTypeName.lastIndexOf('.'); + return (pos < 0) ? "" : qualifiedTypeName.substring(0, pos); + } + + public static String getPackageNameFromBinary(String binaryName) { + int pos = binaryName.lastIndexOf('/'); + return (pos < 0) ? "" : binaryName.substring(0, pos).replace('/', '.'); + } + + public static String getShortName(String qualifiedTypeName) { + int pos = qualifiedTypeName.lastIndexOf('.'); + return (pos < 0) ? qualifiedTypeName : qualifiedTypeName.substring(pos + 1); + } + + public static String makeTypeName(String packageName, String shortName) { + if (packageName.length() == 0) { + return shortName; + } else { + return packageName + '.' + shortName; + } + } + + public static String readContent(InputStream content) { + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] buf = new byte[1024]; + for (int readCount = content.read(buf); readCount > 0; readCount = content.read(buf)) { + out.write(buf, 0, readCount); + } + return Util.toString(out.toByteArray()); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + Utility.close(content); + } + } + + public static String toPath(String qualifiedTypeName) { + return qualifiedTypeName.replace('.', '/') + ".java"; + } + + public static String toTypeName(String path) { + assert (path.endsWith(".java")); + path = path.substring(0, path.lastIndexOf('.')); + return path.replace('/', '.'); + } + + static String[] modifierBitsToNames(int bits) { + List<String> strings = new ArrayList<String>(); + + // The order is based on the order in which we want them to appear. + // + if (0 != (bits & MOD_PUBLIC)) { + strings.add("public"); + } + + if (0 != (bits & MOD_PRIVATE)) { + strings.add("private"); + } + + if (0 != (bits & MOD_PROTECTED)) { + strings.add("protected"); + } + + if (0 != (bits & MOD_STATIC)) { + strings.add("static"); + } + + if (0 != (bits & MOD_ABSTRACT)) { + strings.add("abstract"); + } + + if (0 != (bits & MOD_FINAL)) { + strings.add("final"); + } + + if (0 != (bits & MOD_NATIVE)) { + strings.add("native"); + } + + if (0 != (bits & MOD_TRANSIENT)) { + strings.add("transient"); + } + + if (0 != (bits & MOD_VOLATILE)) { + strings.add("volatile"); + } + + return strings.toArray(NO_STRINGS); + } +}
diff --git a/dev/core/src/com/google/gwt/dev/javac/impl/SourceFileCompilationUnit.java b/dev/core/src/com/google/gwt/dev/javac/impl/SourceFileCompilationUnit.java new file mode 100644 index 0000000..4ad2541 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/javac/impl/SourceFileCompilationUnit.java
@@ -0,0 +1,65 @@ +/* + * Copyright 2008 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.impl; + +import com.google.gwt.dev.javac.CompilationUnit; +import com.google.gwt.dev.javac.JavaSourceFile; + +/** + * A compilation unit that was generated. + */ +public class SourceFileCompilationUnit extends CompilationUnit { + + private String sourceCode; + private JavaSourceFile sourceFile; + + public SourceFileCompilationUnit(JavaSourceFile sourceFile) { + this.sourceFile = sourceFile; + } + + @Override + public String getDisplayLocation() { + return sourceFile.getLocation(); + } + + @Override + public String getSource() { + if (sourceCode == null) { + sourceCode = sourceFile.readSource(); + } + return sourceCode; + } + + public JavaSourceFile getSourceFile() { + return sourceFile; + } + + @Override + public String getTypeName() { + return sourceFile.getTypeName(); + } + + @Override + public boolean isGenerated() { + return false; + } + + @Override + protected void dumpSource() { + sourceCode = null; + } + +}
diff --git a/dev/core/src/com/google/gwt/dev/jdt/AbstractCompiler.java b/dev/core/src/com/google/gwt/dev/jdt/AbstractCompiler.java index b575d62..885ef4b 100644 --- a/dev/core/src/com/google/gwt/dev/jdt/AbstractCompiler.java +++ b/dev/core/src/com/google/gwt/dev/jdt/AbstractCompiler.java
@@ -16,9 +16,12 @@ package com.google.gwt.dev.jdt; import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.TreeLogger.HelpInfo; -import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider; +import com.google.gwt.dev.javac.CompilationState; +import com.google.gwt.dev.javac.CompilationUnit; +import com.google.gwt.dev.javac.GWTProblem; +import com.google.gwt.dev.javac.JdtCompiler.CompilationUnitAdapter; +import com.google.gwt.dev.javac.impl.Shared; import com.google.gwt.dev.util.CharArrayComparator; import com.google.gwt.dev.util.Empty; import com.google.gwt.dev.util.PerfLogger; @@ -59,22 +62,6 @@ public abstract class AbstractCompiler { /** - * A policy that can be set to affect which - * {@link org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration - * CompilationUnitDeclarations} the compiler processes. - */ - public interface CachePolicy { - - /** - * Return true if <code>cud</code> should be processed, otherwise false. - * - * @param cud a not <code>null</code> unit - * @return true iff <code>cud</code> should be fully processed - */ - boolean shouldProcess(CompilationUnitDeclaration cud); - } - - /** * Adapted to hook the processing of compilation unit declarations so as to be * able to add additional compilation units based on the results of * previously-compiled ones. Examples of cases where this is useful include @@ -105,11 +92,6 @@ long processBeginNanos = System.nanoTime(); - if (!cachePolicy.shouldProcess(cud)) { - jdtProcessNanos += System.nanoTime() - processBeginNanos; - return; - } - // The following block of code is a copy of super.process(cud, index), // with the modification that cud.generateCode is conditionally called // based on doGenerateBytes @@ -313,34 +295,11 @@ TreeLogger logger = threadLogger.branch(TreeLogger.SPAM, "Compiler is asking about '" + qname + "'", null); - if (sourceOracle.isPackage(qname)) { + if (isPackage(qname)) { logger.log(TreeLogger.SPAM, "Found to be a package", null); return null; } - // Try to find the compiled type in the cache. - // - ByteCode byteCode = doGetByteCodeFromCache(logger, qname); - if (byteCode != null) { - // Return it as a binary type to JDT. - // - byte[] classBytes = byteCode.getBytes(); - char[] loc = byteCode.getLocation().toCharArray(); - try { - logger.log(TreeLogger.SPAM, "Found cached bytes", null); - ClassFileReader cfr = new ClassFileReader(classBytes, loc); - NameEnvironmentAnswer out = new NameEnvironmentAnswer(cfr, null); - nameEnvironmentAnswerForTypeName.put(qname, out); - return out; - } catch (ClassFormatException e) { - // Bad bytecode in the cache. Remove it from the cache. - // - String msg = "Bad bytecode for '" + qname + "'"; - compiler.problemReporter.abortDueToInternalError(msg); - return null; - } - } - // Didn't find it in the cache, so let's compile from source. // Strip off the inner types, if any // @@ -353,49 +312,40 @@ return (nameEnvironmentAnswerForTypeName.get(qname)); } } - CompilationUnitProvider cup; - try { - cup = sourceOracle.findCompilationUnit(logger, qname); - if (cup != null) { - logger.log(TreeLogger.SPAM, "Found type in compilation unit: " - + cup.getLocation(), null); - ICompilationUnitAdapter unit = new ICompilationUnitAdapter(cup); - NameEnvironmentAnswer out = new NameEnvironmentAnswer(unit, null); - nameEnvironmentAnswerForTypeName.put(qname, out); - return out; - } else { - ClassLoader classLoader = getClassLoader(); - URL resourceURL = classLoader.getResource(className.replace('.', '/') - + ".class"); - if (resourceURL != null) { - /* - * We know that there is a .class file that matches the name that we - * are looking for. However, at least on OSX, this lookup is case - * insensitive so we need to use Class.forName to effectively verify - * the case. - */ - if (isBinaryType(classLoader, className)) { - byte[] classBytes = Util.readURLAsBytes(resourceURL); - ClassFileReader cfr; - try { - cfr = new ClassFileReader(classBytes, null); - NameEnvironmentAnswer out = new NameEnvironmentAnswer(cfr, null); - nameEnvironmentAnswerForTypeName.put(qname, out); - return out; - } catch (ClassFormatException e) { - // Ignored. - } + CompilationUnit unit = findCompilationUnit(qname); + if (unit != null) { + logger.log(TreeLogger.SPAM, "Found type in compilation unit: " + + unit.getDisplayLocation()); + ICompilationUnit icu = new CompilationUnitAdapter(unit); + NameEnvironmentAnswer out = new NameEnvironmentAnswer(icu, null); + nameEnvironmentAnswerForTypeName.put(qname, out); + return out; + } else { + ClassLoader classLoader = getClassLoader(); + URL resourceURL = classLoader.getResource(className.replace('.', '/') + + ".class"); + if (resourceURL != null) { + /* + * We know that there is a .class file that matches the name that we + * are looking for. However, at least on OSX, this lookup is case + * insensitive so we need to use Class.forName to effectively verify + * the case. + */ + if (isBinaryType(classLoader, className)) { + byte[] classBytes = Util.readURLAsBytes(resourceURL); + ClassFileReader cfr; + try { + cfr = new ClassFileReader(classBytes, null); + NameEnvironmentAnswer out = new NameEnvironmentAnswer(cfr, null); + nameEnvironmentAnswerForTypeName.put(qname, out); + return out; + } catch (ClassFormatException e) { + // Ignored. } } - - logger.log(TreeLogger.SPAM, "Not a known type", null); - return null; } - } catch (UnableToCompleteException e) { - // It was found, but something went really wrong trying to get it. - // - String msg = "Error acquiring source for '" + qname + "'"; - compiler.problemReporter.abortDueToInternalError(msg); + + logger.log(TreeLogger.SPAM, "Not a known type", null); return null; } } @@ -409,7 +359,7 @@ String packageName = String.valueOf(pathChars); if (knownPackages.contains(packageName)) { return true; - } else if (sourceOracle.isPackage(packageName) + } else if (isPackage(packageName) || isPackage(getClassLoader(), packageName)) { // Grow our own list to spare calls into the host. // @@ -442,6 +392,10 @@ String packageAsPath = packageName.replace('.', '/'); return classLoader.getResource(packageAsPath) != null; } + + private boolean isPackage(String packageName) { + return packages.contains(packageName); + } } private static final Comparator<CompilationUnitDeclaration> CUD_COMPARATOR = new Comparator<CompilationUnitDeclaration>() { @@ -468,16 +422,10 @@ } }; - private static final CachePolicy DEFAULT_POLICY = new CachePolicy() { - public boolean shouldProcess(CompilationUnitDeclaration cud) { - return true; - } - }; + protected final CompilationState compilationState; protected final ThreadLocalTreeLoggerProxy threadLogger = new ThreadLocalTreeLoggerProxy(); - private CachePolicy cachePolicy = DEFAULT_POLICY; - private final CompilerImpl compiler; private final boolean doGenerateBytes; @@ -486,12 +434,11 @@ private final Map<String, NameEnvironmentAnswer> nameEnvironmentAnswerForTypeName = new HashMap<String, NameEnvironmentAnswer>(); - private final SourceOracle sourceOracle; + private final Set<String> packages = new HashSet<String>(); - private final Map<String, ICompilationUnit> unitsByTypeName = new HashMap<String, ICompilationUnit>(); - - protected AbstractCompiler(SourceOracle sourceOracle, boolean doGenerateBytes) { - this.sourceOracle = sourceOracle; + protected AbstractCompiler(CompilationState compilationState, + boolean doGenerateBytes) { + this.compilationState = compilationState; this.doGenerateBytes = doGenerateBytes; rememberPackage(""); @@ -528,30 +475,14 @@ probFact); } - public CachePolicy getCachePolicy() { - return cachePolicy; - } - - public final void invalidateUnitsInFiles(Set<String> typeNames) { - // StandardSourceOracle has its own cache that needs to be cleared - // out. Short of modifying the interface SourceOracle to have an - // invalidateCups, this check is needed. - if (sourceOracle instanceof StandardSourceOracle) { - StandardSourceOracle sso = (StandardSourceOracle) sourceOracle; - sso.invalidateCups(typeNames); - } - for (String qname : typeNames) { - unitsByTypeName.remove(qname); - nameEnvironmentAnswerForTypeName.remove(qname); - } - } - - public void setCachePolicy(CachePolicy policy) { - this.cachePolicy = policy; - } - protected final CompilationUnitDeclaration[] compile(TreeLogger logger, ICompilationUnit[] units) { + // Initialize the packages list. + for (CompilationUnit unit : compilationState.getCompilationUnits()) { + String packageName = Shared.getPackageName(unit.getTypeName()); + addPackages(packageName); + } + // Any additional compilation units that are found to be needed will be // pulled in while procssing compilation units. See CompilerImpl.process(). // @@ -596,53 +527,18 @@ return Empty.STRINGS; } - /** - * Checks to see if we already have the bytecode definition of the requested - * type. By default we compile everything from source, so we never have it - * unless a subclass overrides this method. - */ - @SuppressWarnings("unused") - // overrider may use unused parameter - protected ByteCode doGetByteCodeFromCache(TreeLogger logger, - String binaryTypeName) { - return null; - } - - /** - * Finds a compilation unit for the given type. This is often used to - * bootstrap compiles since during compiles, the compiler will directly ask - * the name environment internally, bypassing this call. - */ - protected ICompilationUnit getCompilationUnitForType(TreeLogger logger, - String binaryTypeName) throws UnableToCompleteException { - - // We really look for the topmost type rather than a nested type. - // - String top = stripNestedTypeNames(binaryTypeName); - - // Check the cache. - // - ICompilationUnit unit = unitsByTypeName.get(top); - if (unit != null) { - return unit; + protected CompilationUnit findCompilationUnit(String qname) { + // Build the initial set of compilation units. + Map<String, CompilationUnit> unitMap = compilationState.getCompilationUnitMap(); + CompilationUnit unit = unitMap.get(qname); + while (unit == null) { + int pos = qname.lastIndexOf('.'); + if (pos < 0) { + return null; + } + qname = qname.substring(0, pos); + unit = unitMap.get(qname); } - - // Not cached, so actually look for it. - // - CompilationUnitProvider cup = sourceOracle.findCompilationUnit(logger, top); - if (cup == null) { - // Could not find the starting type. - // - String s = "Unable to find compilation unit for type '" + top + "'"; - logger.log(TreeLogger.WARN, s, null); - throw new UnableToCompleteException(); - } - - // Create a cup adapter and cache it. - // - unit = new ICompilationUnitAdapter(cup); - unitsByTypeName.put(top, unit); - return unit; } @@ -673,20 +569,19 @@ return compiler.resolvePossiblyNestedType(typeName); } - SourceOracle getSourceOracle() { - return sourceOracle; - } - - private String stripNestedTypeNames(String binaryTypeName) { - int i = binaryTypeName.lastIndexOf('.'); - if (i == -1) { - i = 0; + private void addPackages(String packageName) { + if (packages.contains(packageName)) { + return; } - int j = binaryTypeName.indexOf('$', i); - if (j != -1) { - return binaryTypeName.substring(0, j); - } else { - return binaryTypeName; + while (true) { + packages.add(String.valueOf(packageName)); + int pos = packageName.lastIndexOf('.'); + if (pos > 0) { + packageName = packageName.substring(0, pos); + } else { + packages.add(""); + break; + } } } }
diff --git a/dev/core/src/com/google/gwt/dev/jdt/AstCompiler.java b/dev/core/src/com/google/gwt/dev/jdt/AstCompiler.java deleted file mode 100644 index 5ae0c7e..0000000 --- a/dev/core/src/com/google/gwt/dev/jdt/AstCompiler.java +++ /dev/null
@@ -1,137 +0,0 @@ -/* - * Copyright 2008 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.jdt; - -import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider; - -import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; -import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; - -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * A facade around the JDT compiler to make many repeated compiles as fast as - * possible, where the result of each compile is a fully-resolved abstract - * syntax tree that can be walked for detailed analysis. - */ -public class AstCompiler extends AbstractCompiler { - - /** - * Manages the caching of <code>CompilationUnitDeclaration</code>. - */ - private class CompilationUnitDeclarationCache { - - private final Map<String, Long> lastModified = new HashMap<String, Long>(); - - private final Map<String, ArrayList<CompilationUnitDeclaration>> map = new HashMap<String, ArrayList<CompilationUnitDeclaration>>(); - - public void remove(String newLoc) { - map.remove(newLoc); - } - - private void add(String location, CompilationUnitDeclaration item) { - File file = new File(location); - if (file.exists()) { - lastModified.put(location, new Long(file.lastModified())); - } - if (!map.containsKey(location)) { - map.put(location, new ArrayList<CompilationUnitDeclaration>()); - } - get(location).add(item); - } - - private boolean containsKey(String location) { - return map.containsKey(location); - } - - private List<CompilationUnitDeclaration> get(Object key) { - return map.get(key); - } - - private void removeAll(Collection<String> c) { - map.keySet().removeAll(c); - } - } - - private final CompilationUnitDeclarationCache cachedResults = new CompilationUnitDeclarationCache(); - private final boolean disableChecks; - - public AstCompiler(SourceOracle sourceOracle) { - super(sourceOracle, false); - this.disableChecks = false; - } - - public AstCompiler(SourceOracle sourceOracle, boolean disableChecks) { - super(sourceOracle, false); - this.disableChecks = disableChecks; - } - - public CompilationUnitDeclaration[] getChangedCompilationUnitDeclarations( - TreeLogger logger, ICompilationUnit[] units) { - List<ICompilationUnit> allUnits = Arrays.asList(units); - List<ICompilationUnitAdapter> newUnits = new ArrayList<ICompilationUnitAdapter>(); - - // Check for newer units that need to be processed. - for (Iterator<ICompilationUnit> iter = allUnits.iterator(); iter.hasNext();) { - ICompilationUnitAdapter adapter = (ICompilationUnitAdapter) iter.next(); - CompilationUnitProvider cup = adapter.getCompilationUnitProvider(); - String location = cup.getLocation(); - if (!(cachedResults.containsKey(location))) { - newUnits.add(adapter); - } - } - ICompilationUnit[] toBeProcessed = new ICompilationUnit[newUnits.size()]; - newUnits.toArray(toBeProcessed); - CompilationUnitDeclaration[] newCuds = compile(logger, toBeProcessed); - - // Put new cuds into cache. - List<CompilationUnitDeclaration> cudResults = new ArrayList<CompilationUnitDeclaration>(); - for (int i = 0; i < newCuds.length; i++) { - CompilationUnitDeclaration newCud = newCuds[i]; - String newLoc = String.valueOf(newCud.getFileName()); - cachedResults.remove(newLoc); - cachedResults.add(newLoc, newCud); - cudResults.add(newCud); - } - - return cudResults.toArray(new CompilationUnitDeclaration[] {}); - } - - public void invalidateChangedFiles(Set<String> changedFiles, - Set<String> typeNames) { - cachedResults.removeAll(changedFiles); - invalidateUnitsInFiles(typeNames); - } - - @Override - protected void doCompilationUnitDeclarationValidation( - CompilationUnitDeclaration cud, TreeLogger logger) { - if (!disableChecks) { - JSORestrictionsChecker.check(cud); - LongFromJSNIChecker.check(cud); - BinaryTypeReferenceRestrictionsChecker.check(cud); - } - } -}
diff --git a/dev/core/src/com/google/gwt/dev/jdt/ByteCode.java b/dev/core/src/com/google/gwt/dev/jdt/ByteCode.java deleted file mode 100644 index d44dcf3..0000000 --- a/dev/core/src/com/google/gwt/dev/jdt/ByteCode.java +++ /dev/null
@@ -1,89 +0,0 @@ -/* - * Copyright 2006 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.jdt; - -import com.google.gwt.dev.About; - -import java.io.Serializable; - -/** - * Represents bytecode for a cached class. - */ -public class ByteCode implements Serializable { - - private static final String systemString = System.getProperty( - "java.class.path", "."); - - private static final String systemStringAsIdentifier = About.GWT_VERSION - + "_" + systemString.hashCode() + "_jsniMethods"; - - /** - * This method returns the current system identifier, used to detect changes - * in the environment that would make cached data unusable. - * - * @return the current system identifier, which is sensitive to classpath and - * os changes as well as GWT version changes - */ - public static String getCurrentSystemIdentifier() { - return systemStringAsIdentifier; - } - - private final String binaryTypeName; - - private final byte[] bytes; - - private final String location; - - private final String version; - - private final boolean isTransient; - - /** - * Specifies the bytecode for a given type. - */ - public ByteCode(String binaryTypeName, byte[] bytes, String location, - boolean isTransient) { - this.binaryTypeName = binaryTypeName; - this.bytes = bytes; - this.location = location; - this.version = systemStringAsIdentifier; - this.isTransient = isTransient; - } - - public String getBinaryTypeName() { - return binaryTypeName; - } - - public byte[] getBytes() { - return bytes; - } - - public String getLocation() { - return location; - } - - public String getSystemIdentifier() { - return version; - } - - public boolean isTransient() { - return isTransient; - } - - // We explicitly do not set serialVersionUID, as it is generated - // automatically, and is more sensitive to class file changes than if - // it were generated manually. -}
diff --git a/dev/core/src/com/google/gwt/dev/jdt/ByteCodeCompiler.java b/dev/core/src/com/google/gwt/dev/jdt/ByteCodeCompiler.java deleted file mode 100644 index f1d9c54..0000000 --- a/dev/core/src/com/google/gwt/dev/jdt/ByteCodeCompiler.java +++ /dev/null
@@ -1,180 +0,0 @@ -/* - * Copyright 2007 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.jdt; - -import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.core.ext.UnableToCompleteException; - -import org.eclipse.jdt.core.compiler.CharOperation; -import org.eclipse.jdt.internal.compiler.ClassFile; -import org.eclipse.jdt.internal.compiler.CompilationResult; -import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; - -/** - * A facade around the JDT compiler to manage on-demand Java source to bytecode - * compilation, caching compiled bytecode where possible. - */ -public class ByteCodeCompiler extends AbstractCompiler { - - private final CacheManager cacheManager; - - /** - * Creates a bytecode compiler for use not in hosted mode. All bytecode will - * be thrown away after reload. - * - * @param sourceOracle used to find the source - */ - public ByteCodeCompiler(SourceOracle sourceOracle) { - super(sourceOracle, true); - this.cacheManager = new CacheManager(); - } - - /** - * Creates a byte code compiler given the supplied sourceOracle (to find the - * source) and the supplied cacheManager (to keep the bytecode and other - * info). If the cacheManager has a cacheDir, it will keep bytecode across - * reload, and load them from the cacheDir on startup. Otherwise, each reload - * will clear the cache. In hosted mode, the cacheManager used to create this - * object should be the same one used to create the typeOracleBuilder. - * - * @param sourceOracle used to find the source - * @param cacheManager used to keep the cached information - */ - public ByteCodeCompiler(SourceOracle sourceOracle, CacheManager cacheManager) { - super(sourceOracle, true); - this.cacheManager = cacheManager; - } - - /** - * Get the bytecode for the specified type. - * - * @param binaryTypeName the binary type name to look up or compile - */ - public byte[] getClassBytes(TreeLogger logger, String binaryTypeName) - throws UnableToCompleteException { - - // We use a thread logger proxy because we can't wind the logger through - // JDT directly. - // - String msg = "Getting bytecode for '" + binaryTypeName + "'"; - logger = logger.branch(TreeLogger.SPAM, msg, null); - - TreeLogger oldLogger = threadLogger.push(logger); - try { - - // Check the bytecode cache in case we've already compiled it. - // - ByteCode byteCode = doGetByteCodeFromCache(logger, binaryTypeName); - if (byteCode != null) { - // We have it already. - // - return byteCode.getBytes(); - } - - // Need to compile it. It could be the case that we have tried before and - // failed, but on the off chance that it's been fixed since then, we adopt - // a policy of always trying to recompile if we don't have it cached. - // - ICompilationUnit start = getCompilationUnitForType(logger, binaryTypeName); - compile(logger, new ICompilationUnit[] {start}); - - // Check the cache again. If it's there now, we succeeded. - // If it isn't there now, we've already logged the error. - // - byteCode = doGetByteCodeFromCache(logger, binaryTypeName); - if (byteCode != null) { - return byteCode.getBytes(); - } else { - throw new UnableToCompleteException(); - } - } finally { - threadLogger.pop(oldLogger); - } - } - - /** - * Prevents the compile process from ever trying to compile these types from - * source. This is used for special types that would not compile correctly - * from source. - * - * @param binaryTypeName the binary name of the specified type - */ - public void putClassBytes(TreeLogger logger, String binaryTypeName, - byte[] bytes, String location) { - - // We must remember the package name independently in case this is a type - // the host doesn't actually know about. - // - String pkgName = ""; - int lastDot = binaryTypeName.lastIndexOf('.'); - if (lastDot != -1) { - pkgName = binaryTypeName.substring(0, lastDot); - } - rememberPackage(pkgName); - - // Cache the bytes. - // - ByteCode byteCode = new ByteCode(binaryTypeName, bytes, location, true); - cacheManager.acceptIntoCache(logger, binaryTypeName, byteCode); - } - - /** - * This method removes the bytecode which is no longer current, or if the - * cacheManager does not have a cacheDir, all the bytecode. - * - * @param logger used to describe the results to the user - */ - public void removeStaleByteCode(TreeLogger logger) { - cacheManager.removeStaleByteCode(logger, this); - } - - @Override - protected void doAcceptResult(CompilationResult result) { - // Take all compiled class files and put them in the byte cache. - // - TreeLogger logger = getLogger(); - ClassFile[] classFiles = result.getClassFiles(); - for (int i = 0; i < classFiles.length; i++) { - ClassFile classFile = classFiles[i]; - char[][] compoundName = classFile.getCompoundName(); - char[] classNameChars = CharOperation.concatWith(compoundName, '.'); - String className = String.valueOf(classNameChars); - byte bytes[] = classFile.getBytes(); - String loc = String.valueOf(result.compilationUnit.getFileName()); - boolean isTransient = true; - if (result.compilationUnit instanceof ICompilationUnitAdapter) { - ICompilationUnitAdapter unit = (ICompilationUnitAdapter) result.compilationUnit; - isTransient = unit.getCompilationUnitProvider().isTransient(); - } - ByteCode byteCode = new ByteCode(className, bytes, loc, isTransient); - if (cacheManager.acceptIntoCache(logger, className, byteCode)) { - logger.log(TreeLogger.SPAM, "Successfully compiled and cached '" - + className + "'", null); - } - } - } - - /** - * Checks the cache for bytecode for the specified binary type name. Silently - * removes and pretends it didn't see any bytecode that is out-of-date with - * respect to the compilation unit that provides it. - */ - @Override - protected ByteCode doGetByteCodeFromCache(TreeLogger logger, - String binaryTypeName) { - return cacheManager.getByteCode(binaryTypeName); - } -}
diff --git a/dev/core/src/com/google/gwt/dev/jdt/CacheManager.java b/dev/core/src/com/google/gwt/dev/jdt/CacheManager.java deleted file mode 100644 index c6895db..0000000 --- a/dev/core/src/com/google/gwt/dev/jdt/CacheManager.java +++ /dev/null
@@ -1,1025 +0,0 @@ -/* - * Copyright 2008 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.jdt; - -import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider; -import com.google.gwt.core.ext.typeinfo.HasMetaData; -import com.google.gwt.core.ext.typeinfo.JClassType; -import com.google.gwt.core.ext.typeinfo.JType; -import com.google.gwt.core.ext.typeinfo.TypeOracle; -import com.google.gwt.dev.shell.JavaScriptHost; -import com.google.gwt.dev.shell.JsniMethods; -import com.google.gwt.dev.shell.ShellGWT; -import com.google.gwt.dev.shell.ShellJavaScriptHost; -import com.google.gwt.dev.shell.JsniMethods.JsniMethod; -import com.google.gwt.dev.util.PerfLogger; -import com.google.gwt.dev.util.Util; - -import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; -import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; -import org.eclipse.jdt.internal.compiler.ast.Javadoc; -import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; -import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; -import org.eclipse.jdt.internal.compiler.impl.ReferenceContext; -import org.eclipse.jdt.internal.compiler.lookup.ClassScope; -import org.eclipse.jdt.internal.compiler.lookup.MethodScope; -import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; -import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.util.AbstractMap; -import java.util.HashMap; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; - -/** - * CacheManager manages all the caching used to speed up hosted mode startup and - * refresh, and manages the invalidations required to ensure that changes are - * reflected correctly on reload. - */ -public class CacheManager { - - /** - * Maps SourceTypeBindings to their associated types. - */ - static class Mapper { - private final Map<ReferenceBinding, JClassType> map = new IdentityHashMap<ReferenceBinding, JClassType>(); - - public JClassType get(ReferenceBinding referenceBinding) { - JClassType type = map.get(referenceBinding); - return type; - } - - public void put(ReferenceBinding binding, JClassType type) { - boolean firstPut = (null == map.put(binding, type)); - assert (firstPut); - } - - public void reset() { - map.clear(); - } - } - - /** - * This class is a very simple multi-valued map. - */ - private static class Dependencies { - private Map<String, HashSet<String>> map = new HashMap<String, HashSet<String>>(); - - /** - * This method adds <code>item</code> to the list stored under - * <code>key</code>. - * - * @param key the key used to access the list - * @param item the item to be added to the list - */ - private void add(String dependerFilename, String dependeeFilename) { - if (!map.containsKey(dependeeFilename)) { - map.put(dependeeFilename, new HashSet<String>()); - } - - get(dependeeFilename).add(dependerFilename); - } - - /** - * This method gets the list stored under <code>key</code>. - * - * @param key the key used to access the list. - * @return the list stored under <code>key</code> - */ - private Set<String> get(String filename) { - return map.get(filename); - } - - private Set<String> transitiveClosure(final String filename) { - String current = filename; - TreeSet<String> queue = new TreeSet<String>(); - Set<String> finished = new HashSet<String>(); - queue.add(filename); - while (true) { - finished.add(current); - Set<String> children = get(current); - if (children != null) { - for (Iterator<String> iter = children.iterator(); iter.hasNext();) { - String child = iter.next(); - if (!finished.contains(child)) { - queue.add(child); - } - } - } - if (queue.size() == 0) { - return finished; - } else { - current = queue.first(); - queue.remove(current); - } - } - } - } - - /** - * Visit all of the CUDs and extract dependencies. This visitor handles - * explicit TypeRefs via the onTypeRef method AND it also deals with the - * gwt.typeArgs annotation. - * - * <ol> - * <li>Extract the list of type names from the gwt.typeArgs annotation</li> - * <li>For each type name, locate the CUD that defines it</li> - * <li>Add the referenced CUD as a dependency</li> - * </ol> - */ - private final class DependencyVisitor extends TypeRefVisitor { - private final Dependencies dependencies; - - private DependencyVisitor(Dependencies dependencies) { - this.dependencies = dependencies; - } - - @Override - public void endVisit(FieldDeclaration fieldDeclaration, - final MethodScope scope) { - extractDependenciesFromTypeArgs(fieldDeclaration.javadoc, - scope.referenceContext()); - } - - @Override - public void endVisit(MethodDeclaration methodDeclaration, ClassScope scope) { - extractDependenciesFromTypeArgs(methodDeclaration.javadoc, - scope.referenceContext()); - } - - @Override - protected void onTypeRef(SourceTypeBinding referencedType, - CompilationUnitDeclaration unitOfReferrer) { - // If the referenced type belongs to a compilation unit that - // was changed, then the unit in which it - // is referenced must also be treated as changed. - // - String dependeeFilename = String.valueOf(referencedType.getFileName()); - String dependerFilename = String.valueOf(unitOfReferrer.getFileName()); - - dependencies.add(dependerFilename, dependeeFilename); - } - - private String combine(String[] strings, int startIndex) { - StringBuffer sb = new StringBuffer(); - for (int i = startIndex; i < strings.length; i++) { - String s = strings[i]; - sb.append(s); - } - return sb.toString(); - } - - /** - * Extracts additional dependencies based on the gwt.typeArgs annotation. - * This is not detected by JDT so we need to do it here. We do not perform - * as strict a parse as the TypeOracle would do. - * - * @param javadoc javadoc text - * @param scope scope that contains the definition - * @param isField true if the javadoc is associated with a field - */ - private void extractDependenciesFromTypeArgs(Javadoc javadoc, - final ReferenceContext scope) { - if (javadoc == null) { - return; - } - final char[] source = scope.compilationResult().compilationUnit.getContents(); - - TypeOracleBuilder.parseMetaDataTags(source, new HasMetaData() { - public void addMetaData(String tagName, String[] values) { - assert (values != null); - - if (!TypeOracle.TAG_TYPEARGS.equals(tagName)) { - // don't care about non gwt.typeArgs - return; - } - - if (values.length == 0) { - return; - } - - Set<String> typeNames = new HashSet<String>(); - - /* - * if the first element starts with a "<" then we assume that no - * parameter name was specified - */ - int startIndex = 1; - if (values[0].trim().startsWith("<")) { - startIndex = 0; - } - - extractTypeNamesFromTypeArg(combine(values, startIndex), typeNames); - - Iterator<String> it = typeNames.iterator(); - while (it.hasNext()) { - String typeName = it.next(); - - try { - ICompilationUnit compilationUnit = astCompiler.getCompilationUnitForType( - TreeLogger.NULL, typeName); - - String dependeeFilename = String.valueOf(compilationUnit.getFileName()); - String dependerFilename = String.valueOf(scope.compilationResult().compilationUnit.getFileName()); - - dependencies.add(dependerFilename, dependeeFilename); - - } catch (UnableToCompleteException e) { - // Purposely ignored - } - } - } - - public String[][] getMetaData(String tagName) { - return null; - } - - public String[] getMetaDataTags() { - return null; - } - }, javadoc); - } - - /** - * Extracts the type names referenced from a gwt.typeArgs annotation and - * adds them to the set of type names. - * - * @param typeArg a string containing the type args as the user entered them - * @param typeNames the set of type names referenced in the typeArgs string - */ - private void extractTypeNamesFromTypeArg(String typeArg, - Set<String> typeNames) { - // Remove all whitespace - typeArg = typeArg.replaceAll("\\\\s", ""); - - // Remove anything that is not a raw type name - String[] typeArgs = typeArg.split("[\\[\\]<>,]"); - - for (int i = 0; i < typeArgs.length; ++i) { - if (typeArgs[i].length() > 0) { - typeNames.add(typeArgs[i]); - } - } - } - } - - /** - * Caches information using a directory, with an in memory cache to prevent - * unneeded disk access. - */ - private static class DiskCache extends AbstractMap<String, Object> { - - private class FileEntry implements Map.Entry<String, Object> { - - private File file; - - private FileEntry(File file) { - this.file = file; - } - - private FileEntry(String className) { - this(new File(directory, possiblyAddTmpExtension(className))); - } - - private FileEntry(String className, Object o) { - this(new File(directory, possiblyAddTmpExtension(className))); - setValue(o); - } - - public String getKey() { - return possiblyRemoveTmpExtension(file.getName()); - } - - public Object getValue() { - if (!file.exists()) { - return null; - } - try { - FileInputStream fis = new FileInputStream(file); - ObjectInputStream ois = new ObjectInputStream(fis); - Object out = ois.readObject(); - ois.close(); - fis.close(); - return out; - } catch (IOException e) { - return null; - // If we can't read the file, we can't get the item from the cache. - } catch (ClassNotFoundException e) { - return null; - // The class does not match because the serialUID is not correct - // so we don't want this item anyway. - } - } - - public void remove() { - file.delete(); - } - - public Object setValue(Object value) { - Object o = getValue(); - FileOutputStream fos; - try { - fos = new FileOutputStream(file); - ObjectOutputStream oos = new ObjectOutputStream(fos); - oos.writeObject(value); - oos.close(); - fos.close(); - } catch (IOException e) { - markCacheDirectoryUnusable(); - } - return o; - } - - private long lastModified() { - return file.lastModified(); - } - } - - private final Map<String, Object> cache = new HashMap<String, Object>(); - - // May be set to null after the fact if the cache directory becomes - // unusable. - private File directory; - - public DiskCache(File dirName) { - if (dirName != null) { - directory = dirName; - possiblyCreateCacheDirectory(); - } else { - directory = null; - } - } - - @Override - public void clear() { - cache.clear(); - if (directory != null) { - for (Iterator<String> iter = keySet().iterator(); iter.hasNext();) { - iter.remove(); - } - } - } - - @Override - public Set<Entry<String, Object>> entrySet() { - Set<Entry<String, Object>> out = new HashSet<Entry<String, Object>>() { - @Override - public boolean remove(Object o) { - if (o instanceof Entry) { - Entry<?, ?> entry = (Entry<?, ?>) o; - boolean removed = (DiskCache.this.remove(entry.getKey())) != null; - super.remove(o); - return removed; - } - return false; - } - }; - out.addAll(cache.entrySet()); - // No directory means no persistence. - if (directory != null) { - possiblyCreateCacheDirectory(); - // Add files not yet loaded into this cache. - File[] entries = directory.listFiles(); - for (int i = 0; i < entries.length; i++) { - if (!cache.containsKey(new FileEntry(entries[i]).getKey())) { - out.add(new FileEntry(entries[i])); - } - } - } - return out; - } - - public Object get(String key) { - if (cache.containsKey(key)) { - return cache.get(key); - } - Object value = null; - if (directory != null) { - value = new FileEntry(key).getValue(); - cache.put(key, value); - } - return value; - } - - @Override - public Set<String> keySet() { - Set<String> out = new HashSet<String>() { - @Override - public boolean remove(Object o) { - boolean removed = (DiskCache.this.remove(o)) != null; - super.remove(o); - return removed; - } - }; - out.addAll(cache.keySet()); - // No directory means no persistence. - if (directory != null) { - possiblyCreateCacheDirectory(); - // Add files not yet loaded into this cache. - File[] entries = directory.listFiles(); - for (int i = 0; i < entries.length; i++) { - out.add(new FileEntry(entries[i].getName()).getKey()); - } - } - return out; - } - - @Override - public Object put(String key, Object value) { - return put(key, value, true); - } - - @Override - public Object remove(Object key) { - String fileName = (String) key; - Object out = get(fileName); - // No directory means no persistence. - if (directory != null) { - possiblyCreateCacheDirectory(); - FileEntry e = new FileEntry(fileName); - e.remove(); - } - cache.remove(key); - return out; - } - - private long lastModified(String className) { - if (directory == null) { - // we have no file on disk to refer to, so should return the same result - // as if the file did not exist -- namely 0. - return 0; - } - return new FileEntry(className).lastModified(); - } - - /** - * This method marks the cache directory as being invalid, so we do not try - * to use it. - */ - private void markCacheDirectoryUnusable() { - System.err.println("The directory " + directory.getAbsolutePath() - + " is not usable as a cache directory"); - directory = null; - } - - /** - * This is used to ensure that if something wicked happens to the cache - * directory while we are running, we do not crash. - */ - private void possiblyCreateCacheDirectory() { - directory.mkdirs(); - if (!(directory.exists() && directory.canWrite())) { - markCacheDirectoryUnusable(); - } - } - - private Object put(String key, Object value, boolean persist) { - Object out = get(key); - - // We use toString to match the string value in FileEntry. - cache.remove(key.toString()); - - // Writes the file. - if (persist && directory != null) { - // This writes the file to the disk and is all that is needed. - new FileEntry(key, value); - } - cache.put(key, value); - return out; - } - } - - /** - * The set of all classes whose bytecode needs to exist as bootstrap bytecode - * to be taken as given by the bytecode compiler. - */ - public static final Class<?>[] BOOTSTRAP_CLASSES = new Class<?>[] { - JavaScriptHost.class, ShellJavaScriptHost.class, ShellGWT.class, - JsniMethods.class, JsniMethod.class}; - - /** - * The set of bootstrap classes, which are marked transient, but are - * nevertheless not recompiled each time, as they are bootstrap classes. - */ - private static final Set<String> TRANSIENT_CLASS_NAMES = new HashSet<String>(); - - static { - for (Class<?> c : BOOTSTRAP_CLASSES) { - TRANSIENT_CLASS_NAMES.add(c.getName()); - } - } - - // This method must be outside of DiskCache because of the restriction against - // defining static methods in inner classes. - private static String possiblyAddTmpExtension(Object className) { - String fileName = className.toString(); - if (fileName.indexOf("-") == -1) { - int hashCode = fileName.hashCode(); - String hashCodeStr = Integer.toHexString(hashCode); - while (hashCodeStr.length() < 8) { - hashCodeStr = '0' + hashCodeStr; - } - fileName = fileName + "-" + hashCodeStr + ".tmp"; - } - return fileName; - } - - // This method must be outside of DiskCache because of the restriction against - // defining static methods in inner classes. - private static String possiblyRemoveTmpExtension(Object fileName) { - String className = fileName.toString(); - if (className.indexOf("-") != -1) { - className = className.split("-")[0]; - } - return className; - } - - private final Set<CompilationUnitProvider> addedCups = new HashSet<CompilationUnitProvider>(); - - private final AstCompiler astCompiler; - - private final DiskCache byteCodeCache; - - private final File cacheDir; - - private final Set<String> changedFiles; - - private final Map<String, CompilationUnitDeclaration> cudsByFileName; - - private final Map<String, CompilationUnitProvider> cupsByLocation = new HashMap<String, CompilationUnitProvider>(); - - private boolean firstTime = true; - - /** - * Set of {@link CompilationUnitProvider} locations for all of the compilation - * units generated by {@link com.google.gwt.core.ext.Generator Generator}s. - * - * TODO: This seems like it should be a Set of CUPs rather than a set of CUP - * locations. - */ - private final Set<String> generatedCupLocations = new HashSet<String>(); - - private final Set<String> generatedResources = new HashSet<String>(); - - private final Mapper identityMapper = new Mapper(); - - private final Set<String> invalidatedTypes = new HashSet<String>(); - - private final TypeOracle oracle; - - private final Map<String, Long> timesByLocation = new HashMap<String, Long>(); - - private final Map<String, ICompilationUnitAdapter> unitsByCup = new HashMap<String, ICompilationUnitAdapter>(); - - /** - * Creates a new <code>CacheManager</code>, creating a new - * <code>TypeOracle</code>. This constructor does not specify a cache - * directory, and therefore is to be used in unit tests and executables that - * do not need caching. - */ - public CacheManager() { - this(null, null); - } - - /** - * Creates a new <code>CacheManager</code>, creating a new - * <code>TypeOracle</code>. This constructor uses the specified cacheDir, - * and does cache information across reloads. If the specified cacheDir is - * null, caching across reloads will be disabled. - */ - public CacheManager(String cacheDir, TypeOracle oracle) { - this(cacheDir, oracle, false); - } - - public CacheManager(String cacheDir, TypeOracle oracle, boolean disableChecks) { - if (oracle == null) { - this.oracle = new TypeOracle(); - } else { - this.oracle = oracle; - } - changedFiles = new HashSet<String>(); - cudsByFileName = new HashMap<String, CompilationUnitDeclaration>(); - if (cacheDir != null) { - this.cacheDir = new File(cacheDir); - this.cacheDir.mkdirs(); - byteCodeCache = new DiskCache(new File(cacheDir, "bytecode")); - } else { - this.cacheDir = null; - byteCodeCache = new DiskCache(null); - } - SourceOracleOnTypeOracle sooto = new SourceOracleOnTypeOracle(this.oracle); - astCompiler = new AstCompiler(sooto, disableChecks); - - astCompiler.setCachePolicy(new AstCompiler.CachePolicy() { - public boolean shouldProcess(CompilationUnitDeclaration cud) { - ICompilationUnit unit = cud.compilationResult.compilationUnit; - ICompilationUnitAdapter adapter = ((ICompilationUnitAdapter) unit); - CompilationUnitProvider cup = adapter.getCompilationUnitProvider(); - return getTypeOracle().findType(cup.getPackageName(), - cup.getMainTypeName()) == null; - } - }); - } - - /** - * Creates a new <code>CacheManager</code>, using the supplied - * <code>TypeOracle</code>. This constructor does not specify a cache - * directory, and therefore is to be used in unit tests and executables that - * do not need caching. - */ - public CacheManager(TypeOracle typeOracle) { - this(null, typeOracle); - } - - /** - * Adds the specified {@link CompilationUnitProvider} to the set of CUPs - * generated by {@link com.google.gwt.core.ext.Generator Generator}s. - * Generated <code>CompilationUnitProviders</code> are not cached across - * reloads. - */ - public void addGeneratedCup(CompilationUnitProvider generatedCup) { - assert (generatedCup != null); - - generatedCupLocations.add(generatedCup.getLocation()); - } - - public void addGeneratedResource(String partialPath) { - generatedResources.add(partialPath); - } - - /** - * This method returns the <code>TypeOracle</code> associated with this - * <code>CacheManager</code>. - */ - public TypeOracle getTypeOracle() { - return oracle; - } - - public boolean hasGeneratedResource(String partialPath) { - return generatedResources.contains(partialPath); - } - - /** - * This removes all state changed since the last time the typeOracle was run. - * - * @param typeOracle - */ - public void invalidateOnRefresh(TypeOracle typeOracle) { - - // If a class is changed, the set of classes in the transitive closure - // of "refers to" must be marked changed as well. - // The initial change set is computed in addCompilationUnit. - - changedFiles.addAll(generatedCupLocations); - addDependentsToChangedFiles(); - - for (Iterator<String> iter = changedFiles.iterator(); iter.hasNext();) { - String location = iter.next(); - CompilationUnitProvider cup = getCupsByLocation().get(location); - unitsByCup.remove(location); - Util.invokeInaccessableMethod(TypeOracle.class, - "invalidateTypesInCompilationUnit", - new Class[] {CompilationUnitProvider.class}, typeOracle, - new Object[] {cup}); - } - - /* - * NOTE: It appears that invalidatedTypes is always empty. Therefore the - * AstCompiler may not be properly removing types from the changed files. - */ - astCompiler.invalidateChangedFiles(changedFiles, invalidatedTypes); - } - - /** - * Ensures that all compilation units generated via generators are removed - * from the system so that they will be generated again, and thereby take into - * account input that may have changed since the last reload. - */ - public void invalidateVolatileFiles() { - for (Iterator<CompilationUnitProvider> iter = addedCups.iterator(); iter.hasNext();) { - CompilationUnitProvider cup = iter.next(); - if (isGeneratedCup(cup)) { - iter.remove(); - } - } - generatedResources.clear(); - } - - /** - * This method adds byte. - * - * @param logger - * @param binaryTypeName - * @param byteCode - * @return - */ - boolean acceptIntoCache(TreeLogger logger, String binaryTypeName, - ByteCode byteCode) { - synchronized (byteCodeCache) { - if (getByteCode(binaryTypeName) == null) { - byteCodeCache.put(binaryTypeName, byteCode, (!byteCode.isTransient())); - logger.log(TreeLogger.SPAM, "Cached bytecode for " + binaryTypeName, - null); - return true; - } else { - logger.log(TreeLogger.SPAM, "Bytecode not re-cached for " - + binaryTypeName, null); - return false; - } - } - } - - /** - * Adds this compilation unit if it is not present, or is older. Otherwise - * does nothing. - * - * @throws UnableToCompleteException thrown if we cannot figure out when this - * cup was modified - */ - void addCompilationUnit(CompilationUnitProvider cup) - throws UnableToCompleteException { - Long lastModified = new Long(cup.getLastModified()); - if (isCupUnchanged(cup, lastModified)) { - return; - } - CompilationUnitProvider oldCup = getCup(cup); - if (oldCup != null) { - addedCups.remove(oldCup); - markCupChanged(cup); - } - timesByLocation.put(cup.getLocation(), lastModified); - cupsByLocation.put(cup.getLocation(), cup); - addedCups.add(cup); - } - - /** - * This method modifies the field <code>changedFiles</code> to contain all - * of the additional files that are capable of reaching any of the files - * currently contained within <code>changedFiles</code>. - */ - void addDependentsToChangedFiles() { - final Dependencies dependencies = new Dependencies(); - - DependencyVisitor trv = new DependencyVisitor(dependencies); - - // Find references to type in units that aren't any longer valid. - // - for (CompilationUnitDeclaration cud : cudsByFileName.values()) { - cud.traverse(trv, cud.scope); - } - - Set<String> toTraverse = new HashSet<String>(changedFiles); - for (Iterator<String> iter = toTraverse.iterator(); iter.hasNext();) { - String fileName = iter.next(); - changedFiles.addAll(dependencies.transitiveClosure(fileName)); - } - } - - ICompilationUnit findUnitForCup(CompilationUnitProvider cup) { - if (!unitsByCup.containsKey(cup.getLocation())) { - unitsByCup.put(cup.getLocation(), new ICompilationUnitAdapter(cup)); - } - return unitsByCup.get(cup.getLocation()); - } - - Set<CompilationUnitProvider> getAddedCups() { - return addedCups; - } - - AstCompiler getAstCompiler() { - return astCompiler; - } - - /** - * Gets the bytecode from the cache, rejecting it if an incompatible change - * occurred since it was cached. - */ - ByteCode getByteCode(String binaryTypeName) { - synchronized (byteCodeCache) { - ByteCode byteCode = (ByteCode) byteCodeCache.get(binaryTypeName); - // we do not want bytecode created with a different classpath or os or - // version of GWT. - if ((byteCode != null) - && byteCode.getSystemIdentifier() != null - && (!(byteCode.getSystemIdentifier().equals(ByteCode.getCurrentSystemIdentifier())))) { - byteCodeCache.remove(binaryTypeName); - byteCode = null; - } - if (byteCode != null) { - // Found it. - // - return byteCode; - } else { - // This type has not been compiled before, or we tried but failed. - // - return null; - } - } - } - - Set<String> getChangedFiles() { - return changedFiles; - } - - Map<String, CompilationUnitDeclaration> getCudsByFileName() { - return cudsByFileName; - } - - CompilationUnitProvider getCup(CompilationUnitProvider cup) { - return getCupsByLocation().get(cup.getLocation()); - } - - Object getCupLastUpdateTime(CompilationUnitProvider cup) { - return getTimesByLocation().get(cup.getLocation()); - } - - Map<String, CompilationUnitProvider> getCupsByLocation() { - return cupsByLocation; - } - - Mapper getIdentityMapper() { - return identityMapper; - } - - Map<String, Long> getTimesByLocation() { - return timesByLocation; - } - - JType getTypeForBinding(ReferenceBinding referenceBinding) { - return identityMapper.get(referenceBinding); - } - - /** - * Was this cup, last modified at time lastModified modified since it was last - * processed by the system? - */ - boolean isCupUnchanged(CompilationUnitProvider cup, Long lastModified) { - Long oldTime = (Long) getCupLastUpdateTime(cup); - if (oldTime != null) { - if (oldTime.longValue() >= lastModified.longValue() - && (!cup.isTransient())) { - return true; - } - } - return false; - } - - /** - * This method is called when a cup is known to have changed. This will ensure - * that all the types defined in this cup are invalidated. - * - * @param cup the cup modified - */ - void markCupChanged(CompilationUnitProvider cup) { - changedFiles.add(String.valueOf(cup.getLocation())); - } - - boolean removeFromCache(TreeLogger logger, String binaryTypeName) { - synchronized (byteCodeCache) { - if (getByteCode(binaryTypeName) == null) { - logger.log(TreeLogger.SPAM, "Bytecode for " + binaryTypeName - + " was not cached, so not removing", null); - return false; - } else { - byteCodeCache.remove(binaryTypeName); - logger.log(TreeLogger.SPAM, "Bytecode not re-cached for " - + binaryTypeName, null); - return false; - } - } - } - - /** - * This method removes all of the bytecode which is out of date from the - * bytecode cache. The set of files needing to be changed are going to be the - * set already known to be changed plus those that are out of date in the - * bytecode cache. - */ - void removeStaleByteCode(TreeLogger logger, AbstractCompiler compiler) { - PerfLogger.start("CacheManager.removeStaleByteCode"); - - if (cacheDir == null) { - byteCodeCache.clear(); - return; - } - if (isFirstTime()) { - Set<String> classNames = byteCodeCache.keySet(); - for (Iterator<String> iter = classNames.iterator(); iter.hasNext();) { - String className = iter.next(); - ByteCode byteCode = ((ByteCode) (byteCodeCache.get(className))); - if (byteCode == null) { - iter.remove(); - continue; - } - String qname = byteCode.getBinaryTypeName(); - if (TRANSIENT_CLASS_NAMES.contains(qname)) { - continue; - } - String location = byteCode.getLocation(); - if (byteCode.isTransient()) { - // GWT transient classes; no need to test. - // Either standardGeneratorContext created it - // in which case we already know it is invalid - // or its something like GWT and it lives. - continue; - } - String fileName = Util.findFileName(location); - CompilationUnitDeclaration compilationUnitDeclaration = cudsByFileName.get(location); - if (compilationUnitDeclaration == null) { - changedFiles.add(location); - continue; - } - long srcLastModified = Long.MAX_VALUE; - File srcLocation = new File(fileName); - if (srcLocation.exists()) { - srcLastModified = srcLocation.lastModified(); - } - long byteCodeLastModified = byteCodeCache.lastModified(className); - if (srcLastModified >= byteCodeLastModified) { - changedFiles.add(location); - } - } - addDependentsToChangedFiles(); - } - becomeNotFirstTime(); - invalidateChangedFiles(logger, compiler); - PerfLogger.end(); - } - - void setTypeForBinding(ReferenceBinding binding, JClassType type) { - identityMapper.put(binding, type); - } - - private void becomeNotFirstTime() { - firstTime = false; - } - - /** - * Actually performs the work of removing the invalidated data from the - * system. At this point, changedFiles should be complete. After this method - * is called, changedFiles should now be empty, since all invalidation that is - * needed to be done. - * - * @param logger logs the process - * @param compiler the compiler caches data, so must be invalidated - */ - private void invalidateChangedFiles(TreeLogger logger, - AbstractCompiler compiler) { - Set<String> invalidTypes = new HashSet<String>(); - if (logger.isLoggable(TreeLogger.TRACE)) { - TreeLogger branch = logger.branch(TreeLogger.TRACE, - "The following compilation units have changed since " - + "the last compilation to bytecode", null); - for (Iterator<String> iter = changedFiles.iterator(); iter.hasNext();) { - String filename = iter.next(); - branch.log(TreeLogger.TRACE, filename, null); - } - } - for (String key : byteCodeCache.keySet()) { - ByteCode byteCode = ((ByteCode) (byteCodeCache.get(key))); - if (byteCode != null) { - String location = byteCode.getLocation(); - if (changedFiles.contains(location)) { - String binaryTypeName = byteCode.getBinaryTypeName(); - invalidTypes.add(binaryTypeName); - removeFromCache(logger, binaryTypeName); - } - } - } - compiler.invalidateUnitsInFiles(invalidTypes); - changedFiles.clear(); - } - - private boolean isFirstTime() { - return firstTime; - } - - private boolean isGeneratedCup(CompilationUnitProvider cup) { - return generatedCupLocations.contains(cup.getLocation()); - } -}
diff --git a/dev/core/src/com/google/gwt/dev/jdt/CompilationUnitProviderWithAlternateSource.java b/dev/core/src/com/google/gwt/dev/jdt/CompilationUnitProviderWithAlternateSource.java deleted file mode 100644 index adb8005..0000000 --- a/dev/core/src/com/google/gwt/dev/jdt/CompilationUnitProviderWithAlternateSource.java +++ /dev/null
@@ -1,60 +0,0 @@ -/* - * Copyright 2006 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.jdt; - -import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider; - -/** - * Wraps an existing compilation unit, retaining the metadata of the original - * unit but providing modified source. - */ -public class CompilationUnitProviderWithAlternateSource implements - CompilationUnitProvider { - private final CompilationUnitProvider cup; - - private final char[] source; - - public CompilationUnitProviderWithAlternateSource( - CompilationUnitProvider cup, char[] source) { - this.cup = cup; - this.source = source; - } - - public long getLastModified() throws UnableToCompleteException { - return cup.getLastModified(); - } - - public String getLocation() { - return cup.getLocation(); - } - - public String getMainTypeName() { - return cup.getMainTypeName(); - } - - public String getPackageName() { - return cup.getPackageName(); - } - - public char[] getSource() throws UnableToCompleteException { - return source; - } - - public boolean isTransient() { - return cup.isTransient(); - } -}
diff --git a/dev/core/src/com/google/gwt/dev/jdt/FindDeferredBindingSitesVisitor.java b/dev/core/src/com/google/gwt/dev/jdt/FindDeferredBindingSitesVisitor.java index 33e6eca..e9e1eab 100644 --- a/dev/core/src/com/google/gwt/dev/jdt/FindDeferredBindingSitesVisitor.java +++ b/dev/core/src/com/google/gwt/dev/jdt/FindDeferredBindingSitesVisitor.java
@@ -15,6 +15,8 @@ */ package com.google.gwt.dev.jdt; +import com.google.gwt.dev.javac.GWTProblem; + import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess;
diff --git a/dev/core/src/com/google/gwt/dev/jdt/ICompilationUnitAdapter.java b/dev/core/src/com/google/gwt/dev/jdt/ICompilationUnitAdapter.java deleted file mode 100644 index e483c59..0000000 --- a/dev/core/src/com/google/gwt/dev/jdt/ICompilationUnitAdapter.java +++ /dev/null
@@ -1,73 +0,0 @@ -/* - * Copyright 2008 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.jdt; - -import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider; - -import org.eclipse.jdt.core.compiler.CharOperation; -import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; - -/** - * Implements <code>ICompilationUnit</code> in terms of a - * {@link CompilationUnitProvider}. - */ -public class ICompilationUnitAdapter implements ICompilationUnit { - - private final CompilationUnitProvider cup; - - public ICompilationUnitAdapter(CompilationUnitProvider cup) { - assert (cup != null); - this.cup = cup; - } - - public CompilationUnitProvider getCompilationUnitProvider() { - return cup; - } - - public char[] getContents() { - try { - return cup.getSource(); - } catch (UnableToCompleteException e) { - return null; - } - } - - public char[] getFileName() { - return cup.getLocation().toCharArray(); - } - - /** - * This method is supposed to return the simple class name for this - * compilation unit. Examples of simple class names would be "String", or - * "ArrayList". - * - * <p>Although JDT allows this method to return null in the cases - * where this compilation unit is not a package-info class, JDT never - * constructs a CUP with a null main type, and we should never do so - * either.</p> - */ - public char[] getMainTypeName() { - String typeName = cup.getMainTypeName(); - return typeName.toCharArray(); - } - - public char[][] getPackageName() { - final char[] pkg = cup.getPackageName().toCharArray(); - final char[][] pkgParts = CharOperation.splitOn('.', pkg); - return pkgParts; - } -}
diff --git a/dev/core/src/com/google/gwt/dev/jdt/SourceOracle.java b/dev/core/src/com/google/gwt/dev/jdt/SourceOracle.java deleted file mode 100644 index 9853ec2..0000000 --- a/dev/core/src/com/google/gwt/dev/jdt/SourceOracle.java +++ /dev/null
@@ -1,45 +0,0 @@ -/* - * Copyright 2006 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.jdt; - -import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider; - -/** - * Abstracts the process of determining which source file contains a given Java - * type and specifying whether or not a given name is a package. - */ -public interface SourceOracle { - - /** - * Attempts to find a compilation unit for the specified source type name. - * - * @return <code>null</code> if a compilation unit for the specified type - * was not found or an error prevented the compilation unit from being - * provided - */ - CompilationUnitProvider findCompilationUnit(TreeLogger logger, - String sourceTypeName) throws UnableToCompleteException; - - /** - * Determines whether or not a string is the name of a package. Remember that - * every part of a package name is also a package. For example, the fact that - * <code>java.lang</code> is a package implies that <code>java</code> is - * also a package. - */ - boolean isPackage(String possiblePackageName); -}
diff --git a/dev/core/src/com/google/gwt/dev/jdt/SourceOracleOnTypeOracle.java b/dev/core/src/com/google/gwt/dev/jdt/SourceOracleOnTypeOracle.java deleted file mode 100644 index 28a7eba..0000000 --- a/dev/core/src/com/google/gwt/dev/jdt/SourceOracleOnTypeOracle.java +++ /dev/null
@@ -1,50 +0,0 @@ -/* - * Copyright 2006 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.jdt; - -import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider; -import com.google.gwt.core.ext.typeinfo.JClassType; -import com.google.gwt.core.ext.typeinfo.TypeOracle; - -/** - * Implements a {@link SourceOracle} in terms of a {@link TypeOracle}. - */ -public class SourceOracleOnTypeOracle implements SourceOracle { - - private final TypeOracle typeOracle; - - public SourceOracleOnTypeOracle(TypeOracle typeOracle) { - this.typeOracle = typeOracle; - } - - public CompilationUnitProvider findCompilationUnit(TreeLogger logger, - String sourceTypeName) { - JClassType type = typeOracle.findType(sourceTypeName); - if (type != null) { - return type.getCompilationUnit(); - } - return null; - } - - public boolean isPackage(String possiblePackageName) { - if (typeOracle.findPackage(possiblePackageName) != null) { - return true; - } else { - return false; - } - } -}
diff --git a/dev/core/src/com/google/gwt/dev/jdt/StandardSourceOracle.java b/dev/core/src/com/google/gwt/dev/jdt/StandardSourceOracle.java deleted file mode 100644 index e9c923e..0000000 --- a/dev/core/src/com/google/gwt/dev/jdt/StandardSourceOracle.java +++ /dev/null
@@ -1,200 +0,0 @@ -/* - * Copyright 2006 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.jdt; - -import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider; -import com.google.gwt.core.ext.typeinfo.JClassType; -import com.google.gwt.core.ext.typeinfo.TypeOracle; -import com.google.gwt.dev.util.Util; - -import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -/** - * Provides a mutable compilation service host on top of a - * {@link com.google.gwt.dev.typeinfo.TypeOracle} as well as providing - * subclasses an opportunity to substitute their own source to implement - * special-handling, such as rewriting JSNI source. - */ -public class StandardSourceOracle implements SourceOracle { - - private final Map cupsByTypeName = new HashMap(); - - private final Set knownPackages = new HashSet(); - - private final TypeOracle typeOracle; - - /** - * @param typeOracle answers questions about compilation unit locations - * @param genDir for compilation units whose location does not correspond to a - * URL that can be opened, their source will be written to this - * directory to support debugging, or <code>null</code> if the - * source need not be written to disk - */ - public StandardSourceOracle(TypeOracle typeOracle) { - this.typeOracle = typeOracle; - } - - /** - * Attempts to find the compilation unit for the requested type. Often - * legitimately returns <code>null</code> because the compilation service - * does tests to help determine whether a particular symbol refers to a - * package or a type. - */ - public final CompilationUnitProvider findCompilationUnit(TreeLogger logger, - String typeName) throws UnableToCompleteException { - - // Check the cache first. - // - CompilationUnitProvider cup = (CompilationUnitProvider) cupsByTypeName.get(typeName); - - if (cup != null) { - // Found in cache. - // - return cup; - } - - // See if the type oracle can find it. - // - JClassType type = typeOracle.findType(typeName); - if (type != null) { - // All type oracle types are supposed to know their compilation unit. - // - cup = type.getCompilationUnit(); - assert (cup != null); - } - - // Give the subclass a chance to replace the source. This used for JSNI in - // hosted mode at present but could be used for any source rewriting trick. - // - if (cup != null) { - try { - CompilationUnitProvider specialCup = doFilterCompilationUnit(logger, - typeName, cup); - - if (specialCup != null) { - // Use the cup that the subclass returned instead. Note that even - // though this file may not exist on disk, it is special so we don't - // want users to have to debug into it unless they specifically ask - // to. - // - cup = specialCup; - } - } catch (UnableToCompleteException e) { - String location = cup.getLocation(); - char[] source = cup.getSource(); - Util.maybeDumpSource(logger, location, source, typeName); - throw e; - } - } - - if (cup == null) { - // Did not find a cup for the type. - // This happens commonly and is not a cause for alarm. - // - return null; - } - - // Remember its package and cache it. - // - cupsByTypeName.put(typeName, cup); - return cup; - } - - public TypeOracle getTypeOracle() { - return typeOracle; - } - - /** - * Determines whether or not a particular name is a package name. - */ - public final boolean isPackage(String possiblePackageName) { - if (knownPackages.contains(possiblePackageName)) { - // The quick route -- we've already answered yes to this question. - // OPTIMIZE: cache NOs as well - return true; - } - - if (typeOracle.findPackage(possiblePackageName) != null) { - // Was a package know on the source path. - // - rememberPackage(possiblePackageName); - return true; - } else { - // Not a package. - // - return false; - } - } - - /** - * Subclasses can override this method if they use a special mechanism to find - * the compilation unit for a type. For example, rewriting source code (as - * with JSNI) or preempting the source for a given type (as with - * <code>GWT.create</code>). - * <p> - * Note that subclasses should <em>not</em> call - * <code>super.{@link #findCompilationUnit(TreeLogger, String)}</code> in - * their implementation. - * - * @return <code>null</code> if you want the superclass to use its normal - * mechanism for finding types - */ - protected CompilationUnitProvider doFilterCompilationUnit(TreeLogger logger, - String typeName, CompilationUnitProvider existing) - throws UnableToCompleteException { - return null; - } - - /** - * Subclasses can override this method if they use additional mechanisms to - * find types magically. - * <p> - * Note that subclasses should <em>not</em> call - * <code>super.{@link #findAdditionalTypesUsingMagic(TreeLogger, CompilationUnitDeclaration)}</code> - * in their implementation. - * - * @return <code>null</code> to indicate that no additional types should be - * added - */ - protected String[] doFindAdditionalTypesUsingMagic(TreeLogger logger, - CompilationUnitDeclaration unit) throws UnableToCompleteException { - return null; - } - - void invalidateCups(Set typeNames) { - cupsByTypeName.keySet().removeAll(typeNames); - } - - /** - * Remember that this package was added. Used for generated packages. - */ - private void rememberPackage(String packageName) { - int i = packageName.lastIndexOf('.'); - if (i != -1) { - // Ensure the parent package is also created. - // - rememberPackage(packageName.substring(0, i)); - } - knownPackages.add(packageName); - } -}
diff --git a/dev/core/src/com/google/gwt/dev/jdt/StaticCompilationUnitProvider.java b/dev/core/src/com/google/gwt/dev/jdt/StaticCompilationUnitProvider.java deleted file mode 100644 index 1b314a8..0000000 --- a/dev/core/src/com/google/gwt/dev/jdt/StaticCompilationUnitProvider.java +++ /dev/null
@@ -1,80 +0,0 @@ -/* - * Copyright 2006 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.jdt; - -import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider; - -/** - * Implements a {@link CompilationUnitProvider} as transient (in-memory) source. - */ -public class StaticCompilationUnitProvider implements CompilationUnitProvider { - - private final String packageName; - - private final String simpleTypeName; - - private final char[] source; - - /** - * @param source if <code>null</code>, override this class and return - * source from {@link #getSource()} - */ - public StaticCompilationUnitProvider(String packageName, - String simpleTypeName, char[] source) { - this.packageName = packageName; - this.simpleTypeName = simpleTypeName; - this.source = source; - } - - /** - * Stubbed to return the same value every time. - */ - public long getLastModified() { - return 0; - } - - /** - * Creates a stable name for this compilation unit. - */ - public final String getLocation() { - String prefix = (packageName.length() > 0) ? packageName + "." : ""; - return "transient source for " + prefix + simpleTypeName; - } - - public String getMainTypeName() { - return getTypeName(); - } - - public String getPackageName() { - return packageName; - } - - public char[] getSource() { - return source; - } - - public String getTypeName() { - return simpleTypeName; - } - - public boolean isTransient() { - return true; - } - - public String toString() { - return getLocation(); - } -}
diff --git a/dev/core/src/com/google/gwt/dev/jdt/URLCompilationUnitProvider.java b/dev/core/src/com/google/gwt/dev/jdt/URLCompilationUnitProvider.java deleted file mode 100644 index 0277dc1..0000000 --- a/dev/core/src/com/google/gwt/dev/jdt/URLCompilationUnitProvider.java +++ /dev/null
@@ -1,144 +0,0 @@ -/* - * Copyright 2006 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.jdt; - -import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider; -import com.google.gwt.dev.util.Util; - -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.net.URLConnection; - -/** - * Implements {@link CompilationUnitProvider} in terms of a URL. - */ -public class URLCompilationUnitProvider implements CompilationUnitProvider { - - private static File trySimplify(URL url) { - String s = url.toExternalForm(); - File f = null; - if (s.startsWith("file:")) { - // Strip the file: off, and use the result. If the result - // does not start with file, we cannot simplify. Using URI - // to do the simplification fails for paths with spaces. - // Any number of slashes at the beginning cause no problem for Java, so - // if c:/windows exists so will ///////c:/windows. - f = new File(s.substring(5)); - if (!f.exists()) { - f = null; - } - } - return f; - } - - private final File file; - - private final String location; - - private final String packageName; - - private char[] source; - - private long sourceCurrentTime = Long.MIN_VALUE; - - private final URL url; - - private final String mainTypeName; - - public URLCompilationUnitProvider(URL url, String packageName) { - assert (url != null); - assert (packageName != null); - this.url = url; - - // Files are faster to work with, so use file if available. - this.file = trySimplify(url); - String simpleTypeName; - if (file == null) { - this.location = url.toExternalForm(); - simpleTypeName = new File(url.getPath()).getName(); - } else { - this.location = this.file.getAbsolutePath(); - simpleTypeName = this.file.getName(); - } - - int i = simpleTypeName.lastIndexOf(".java"); - if (i != -1) { - simpleTypeName = simpleTypeName.substring(0, i); - } - mainTypeName = simpleTypeName; - - this.packageName = packageName; - } - - public long getLastModified() throws UnableToCompleteException { - try { - if (file != null) { - return file.lastModified(); - } else { - String converted = Util.findFileName(location); - if (converted != location) { - return new File(converted).lastModified(); - } - URLConnection conn = url.openConnection(); - return conn.getLastModified(); - } - } catch (IOException e) { - throw new UnableToCompleteException(); - } - } - - public String getLocation() { - return location; - } - - public String getMainTypeName() { - return mainTypeName; - } - - public String getPackageName() { - return packageName; - } - - public char[] getSource() throws UnableToCompleteException { - long lastModified = getLastModified(); - if (sourceCurrentTime >= lastModified && source != null) { - return source; - } else { - sourceCurrentTime = lastModified; - } - if (file == null) { - // Pre-read source. - source = Util.readURLAsChars(url); - } else { - source = Util.readFileAsChars(file); - } - if (source == null) { - throw new UnableToCompleteException(); - } - return source; - } - - public boolean isTransient() { - return false; - } - - @Override - public String toString() { - return location; - } -}
diff --git a/dev/core/src/com/google/gwt/dev/jdt/WebModeCompilerFrontEnd.java b/dev/core/src/com/google/gwt/dev/jdt/WebModeCompilerFrontEnd.java index d7efced..689ed08 100644 --- a/dev/core/src/com/google/gwt/dev/jdt/WebModeCompilerFrontEnd.java +++ b/dev/core/src/com/google/gwt/dev/jdt/WebModeCompilerFrontEnd.java
@@ -17,6 +17,9 @@ import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.dev.javac.CompilationState; +import com.google.gwt.dev.javac.CompilationUnit; +import com.google.gwt.dev.javac.JdtCompiler.CompilationUnitAdapter; import com.google.gwt.dev.jdt.FindDeferredBindingSitesVisitor.DeferredBindingSite; import com.google.gwt.dev.util.Empty; import com.google.gwt.dev.util.JsniRef; @@ -36,13 +39,13 @@ * Provides a reusable front-end based on the JDT compiler that incorporates * GWT-specific concepts such as JSNI and deferred binding. */ -public class WebModeCompilerFrontEnd extends AstCompiler { +public class WebModeCompilerFrontEnd extends AbstractCompiler { private final RebindPermutationOracle rebindPermOracle; - public WebModeCompilerFrontEnd(SourceOracle sourceOracle, + public WebModeCompilerFrontEnd(CompilationState compilationState, RebindPermutationOracle rebindPermOracle) { - super(sourceOracle); + super(compilationState, false); this.rebindPermOracle = rebindPermOracle; } @@ -51,17 +54,29 @@ throws UnableToCompleteException { // Build the initial set of compilation units. - // - ICompilationUnit[] units = new ICompilationUnit[seedTypeNames.length]; + Map<String, CompilationUnit> unitMap = compilationState.getCompilationUnitMap(); + ICompilationUnit[] icus = new ICompilationUnit[seedTypeNames.length]; for (int i = 0; i < seedTypeNames.length; i++) { String seedTypeName = seedTypeNames[i]; - units[i] = getCompilationUnitForType(logger, seedTypeName); + CompilationUnit unit = unitMap.get(seedTypeName); + while (unit == null) { + int pos = seedTypeName.lastIndexOf('.'); + if (pos < 0) { + logger.log(TreeLogger.ERROR, + "Unable to find compilation unit for type '" + seedTypeNames[i] + + "'"); + throw new UnableToCompleteException(); + } + seedTypeName = seedTypeName.substring(0, pos); + unit = unitMap.get(seedTypeName); + } + icus[i] = new CompilationUnitAdapter(unit); } // Compile, which will pull in everything else via // doFindAdditionalTypesUsingMagic() // - CompilationUnitDeclaration[] cuds = compile(logger, units); + CompilationUnitDeclaration[] cuds = compile(logger, icus); return cuds; }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java index 99546d5..00590f6 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java +++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -17,8 +17,6 @@ import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider; -import com.google.gwt.dev.jdt.ICompilationUnitAdapter; import com.google.gwt.dev.jdt.RebindOracle; import com.google.gwt.dev.jdt.RebindPermutationOracle; import com.google.gwt.dev.jdt.WebModeCompilerFrontEnd; @@ -254,7 +252,6 @@ private final String[] declEntryPoints; private final CompilationUnitDeclaration[] goldenCuds; - private long lastModified; private final JJSOptions options; private final Set<IProblem> problemSet = new HashSet<IProblem>(); @@ -302,25 +299,6 @@ // found here will have already been logged by AbstractCompiler. // checkForErrors(logger, false); - - // Find the newest of all these. - // - lastModified = 0; - CompilationUnitProvider newestCup = null; - for (CompilationUnitDeclaration cud : goldenCuds) { - ICompilationUnitAdapter icua = (ICompilationUnitAdapter) cud.compilationResult.compilationUnit; - CompilationUnitProvider cup = icua.getCompilationUnitProvider(); - long cupLastModified = cup.getLastModified(); - if (cupLastModified > lastModified) { - newestCup = cup; - lastModified = cupLastModified; - } - } - if (newestCup != null) { - String loc = newestCup.getLocation(); - String msg = "Newest compilation unit is '" + loc + "'"; - logger.log(TreeLogger.DEBUG, msg, null); - } } /** @@ -535,10 +513,6 @@ } } - public long getLastModifiedTimeOfNewestCompilationUnit() { - return lastModified; - } - private void checkForErrors(final TreeLogger logger, boolean itemizeErrors) throws UnableToCompleteException { boolean compilationFailed = false;
diff --git a/dev/core/src/com/google/gwt/dev/resource/Resource.java b/dev/core/src/com/google/gwt/dev/resource/Resource.java new file mode 100644 index 0000000..b5310df --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/resource/Resource.java
@@ -0,0 +1,75 @@ +/* + * Copyright 2008 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.resource; + +import java.io.InputStream; +import java.net.URL; + +/** + * Provides information about a file-like resource. + */ +public abstract class Resource { + + /** + * Overridden to finalize; always returns object identity. + */ + @Override + public final boolean equals(Object obj) { + return super.equals(obj); + } + + /** + * Returns the user-relevant location of the resource. No programmatic + * assumptions should be made about the return value. + */ + public abstract String getLocation(); + + /** + * Returns the full abstract path of the resource. + */ + public abstract String getPath(); + + /** + * Returns a URL for this resource; this URL will only be valid for resources + * based off the file system. + * + * TODO: get rid of this method? + */ + public abstract URL getURL(); + + /** + * Overridden to finalize; always returns identity hash code. + */ + @Override + public final int hashCode() { + return super.hashCode(); + } + + /** + * Returns the contents of the resource. May return <code>null</code> if + * this {@link Resource} has been invalidated by its containing + * {@link ResourceOracle}. The caller is responsible for closing the stream. + */ + public abstract InputStream openContents(); + + /** + * Overridden to finalize; always returns {@link #getLocation()}. + */ + @Override + public final String toString() { + return getLocation(); + } +}
diff --git a/dev/core/src/com/google/gwt/dev/resource/ResourceOracle.java b/dev/core/src/com/google/gwt/dev/resource/ResourceOracle.java new file mode 100644 index 0000000..de67ad5 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/resource/ResourceOracle.java
@@ -0,0 +1,73 @@ +/* + * Copyright 2008 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.resource; + +import java.util.Map; +import java.util.Set; + +/** + * An abstraction for finding and retrieving {@link Resource}s by abstract path + * name. Intuitively, it works like a jar in that each URL is uniquely located + * somewhere in an abstract namespace. The abstract names must be constructed + * from a series of zero or more valid Java identifiers followed by the '/' + * character and finally ending in a valid filename, for example, + * <code>com/google/gwt/blah.txt</code>. + * + * <p> + * The identity of the returned sets and maps will change exactly when the + * underlying module is refreshed. + * </p> + * + * <p> + * Even when the identity of a returned set changes, the identity of any + * contained {@link Resource} values is guaranteed to differ from a previous + * result exactly when that particular resource becomes invalid. + * </p> + * + * <p> + * A resource could become invalid for various reasons, including: + * <ul> + * <li>the underlying file was deleted or modified</li> + * <li>another file with the same logical name superceded it on the classpath</li> + * <li>the underlying module changed to exclude this file or supercede it with + * another file</li> + * </ul> + * </p> + * + * <p> + * After a refresh, a client can reliably detect changes by checking which of + * its cached resource is still contained in the new result of + * {@link #getResources()}. + * </p> + */ +public interface ResourceOracle { + + /** + * Returns an unmodifiable set of unique abstract path names with constant + * lookup time. + */ + Set<String> getPathNames(); + + /** + * Returns an unmodifiable map of abstract path name to resource. + */ + Map<String, Resource> getResourceMap(); + + /** + * Returns an unmodifiable set of unique resources with constant lookup time. + */ + Set<Resource> getResources(); +}
diff --git a/dev/core/src/com/google/gwt/dev/resource/impl/AbstractResource.java b/dev/core/src/com/google/gwt/dev/resource/impl/AbstractResource.java new file mode 100644 index 0000000..99c9fd9 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/resource/impl/AbstractResource.java
@@ -0,0 +1,33 @@ +/* + * Copyright 2008 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.resource.impl; + +import com.google.gwt.dev.resource.Resource; + +/** + * TODO(bruce): write me. + */ +public abstract class AbstractResource extends Resource { + + /** + * Accesses the path root under which this resource was found. Only available + * within this package. + */ + public abstract ClassPathEntry getClassPathEntry(); + + public abstract boolean isStale(); +} +
diff --git a/dev/core/src/com/google/gwt/dev/resource/impl/ClassPathEntry.java b/dev/core/src/com/google/gwt/dev/resource/impl/ClassPathEntry.java new file mode 100644 index 0000000..aaa46f9 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/resource/impl/ClassPathEntry.java
@@ -0,0 +1,49 @@ +/* + * Copyright 2008 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.resource.impl; + +import com.google.gwt.core.ext.TreeLogger; + +import java.util.Set; + +/** + * A location that acts as a starting point for finding resources + * {@link ResourceOracleImpl}. + */ +public abstract class ClassPathEntry { + + /** + * Finds every resource at abstract path P within this classpath such that P + * begins with a prefix X from the path prefix set and P is allowed by the + * filter associated with X. + * + * @return a set of zero or more resources; note no guarantees are made + * regarding the identities of the returned resource objects, and the + * same object may be returned across multiple calls + */ + public abstract Set<AbstractResource> findApplicableResources( + TreeLogger logger, PathPrefixSet pathPrefixSet); + + /** + * Gets a URL string that describes this class path entry. + */ + public abstract String getLocation(); + + @Override + public String toString() { + return getClass().getSimpleName() + ": " + getLocation(); + } +}
diff --git a/dev/core/src/com/google/gwt/dev/resource/impl/DirectoryClassPathEntry.java b/dev/core/src/com/google/gwt/dev/resource/impl/DirectoryClassPathEntry.java new file mode 100644 index 0000000..7d29ca6 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/resource/impl/DirectoryClassPathEntry.java
@@ -0,0 +1,102 @@ +/* + * Copyright 2008 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.resource.impl; + +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.dev.util.msg.Message1String; + +import java.io.File; +import java.util.HashSet; +import java.util.Set; + +/** + * TODO(bruce): write me. + */ +public class DirectoryClassPathEntry extends ClassPathEntry { + + private static class Messages { + static final Message1String NOT_DESCENDING_INTO_DIR = new Message1String( + TreeLogger.SPAM, "Prefix set does not include dir: $0"); + + static final Message1String DESCENDING_INTO_DIR = new Message1String( + TreeLogger.SPAM, "Descending into dir: $0"); + + static final Message1String EXCLUDING_FILE = new Message1String( + TreeLogger.DEBUG, "Filter excludes file: $0"); + + static final Message1String INCLUDING_FILE = new Message1String( + TreeLogger.DEBUG, "Including file: $0"); + } + + private final File dir; + + public DirectoryClassPathEntry(File dir) { + this.dir = dir; + } + + @Override + public Set<AbstractResource> findApplicableResources(TreeLogger logger, + PathPrefixSet pathPrefixSet) { + Set<AbstractResource> results = new HashSet<AbstractResource>(); + descendToFindResources(logger, pathPrefixSet, results, dir, ""); + return results; + } + + @Override + public String getLocation() { + return dir.getAbsoluteFile().toURI().toString(); + } + + /** + * @param logger logs progress + * @param resources the accumulating set of resources found + * @param dir the file or directory to consider + * @param dirPath the abstract path name associated with 'parent', which + * explicitly does not include the classpath entry in its path + */ + private void descendToFindResources(TreeLogger logger, + PathPrefixSet pathPrefixSet, Set<AbstractResource> resources, File dir, + String dirPath) { + assert (dir.isDirectory()); + + // Assert: this directory is included in the path prefix set. + + File[] children = dir.listFiles(); + for (File child : children) { + String childPath = dirPath + child.getName(); + if (child.isDirectory()) { + String childDirPath = childPath + "/"; + if (pathPrefixSet.includesDirectory(childDirPath)) { + Messages.DESCENDING_INTO_DIR.log(logger, child.getAbsolutePath(), + null); + descendToFindResources(logger, pathPrefixSet, resources, child, + childDirPath); + } else { + Messages.NOT_DESCENDING_INTO_DIR.log(logger, child.getAbsolutePath(), + null); + } + } else { + if (pathPrefixSet.includesResource(childPath)) { + Messages.INCLUDING_FILE.log(logger, childPath, null); + FileResource r = new FileResource(this, childPath, child); + resources.add(r); + } else { + Messages.EXCLUDING_FILE.log(logger, childPath, null); + } + } + } + } +}
diff --git a/dev/core/src/com/google/gwt/dev/resource/impl/FileResource.java b/dev/core/src/com/google/gwt/dev/resource/impl/FileResource.java new file mode 100644 index 0000000..74ba79f --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/resource/impl/FileResource.java
@@ -0,0 +1,96 @@ +/* + * Copyright 2008 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.resource.impl; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; + +/** + * Represents a resource contained in directory on a file system. + */ +public class FileResource extends AbstractResource { + + private final String abstractPathName; + private final DirectoryClassPathEntry classPathEntry; + private final File file; + private final long modificationSeconds; + + public FileResource(DirectoryClassPathEntry classPathEntry, + String abstractPathName, File file) { + assert (file.isFile()); + this.classPathEntry = classPathEntry; + this.abstractPathName = abstractPathName; + this.file = file; + this.modificationSeconds = lastModifiedSeconds(file); + } + + @Override + public DirectoryClassPathEntry getClassPathEntry() { + return classPathEntry; + } + + @Override + public String getLocation() { + return file.getAbsoluteFile().toURI().toString(); + } + + @Override + public String getPath() { + return abstractPathName; + } + + @Override + public URL getURL() { + try { + return new URL(getLocation()); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean isStale() { + if (!file.exists()) { + // File was deleted. Always stale. + return true; + } + + long currentModificationSeconds = lastModifiedSeconds(file); + /* + * We use != instead of > because the point is to reflect what's actually on + * the file system, not to worry about freshness per se. + */ + return (currentModificationSeconds != modificationSeconds); + } + + @Override + public InputStream openContents() { + try { + return new FileInputStream(file); + } catch (FileNotFoundException e) { + return null; + } + } + + private long lastModifiedSeconds(File file) { + return file.lastModified() / 1000; + } + +}
diff --git a/dev/core/src/com/google/gwt/dev/resource/impl/JarFileClassPathEntry.java b/dev/core/src/com/google/gwt/dev/resource/impl/JarFileClassPathEntry.java new file mode 100644 index 0000000..7e7de4b --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/resource/impl/JarFileClassPathEntry.java
@@ -0,0 +1,129 @@ +/* + * Copyright 2008 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.resource.impl; + +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.dev.util.msg.Message1String; + +import java.io.File; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * A classpath entry that is a jar file. + */ +public class JarFileClassPathEntry extends ClassPathEntry { + + /** + * Logger messages related to this class. + */ + private static class Messages { + static final Message1String BUILDING_INDEX = new Message1String( + TreeLogger.TRACE, "Indexing jar file: $0"); + + static final Message1String EXCLUDING_RESOURCE = new Message1String( + TreeLogger.DEBUG, "Excluding $0"); + + static final Message1String FINDING_INCLUDED_RESOURCES = new Message1String( + TreeLogger.DEBUG, "Searching for included resources in $0"); + + static final Message1String INCLUDING_RESOURCE = new Message1String( + TreeLogger.DEBUG, "Including $0"); + + static final Message1String READ_JAR_ENTRY = new Message1String( + TreeLogger.DEBUG, "$0"); + } + + private Set<JarFileResource> allJarFileResources; + private Set<AbstractResource> cachedAnswers; + private final JarFile jarFile; + private PathPrefixSet lastPrefixSet; + + public JarFileClassPathEntry(JarFile jarFile) { + this.jarFile = jarFile; + } + + /** + * Indexes the jar file on-demand, and only once over the life of the process. + */ + @Override + public Set<AbstractResource> findApplicableResources(TreeLogger logger, + PathPrefixSet pathPrefixSet) { + // Never re-index. + if (allJarFileResources == null) { + allJarFileResources = buildIndex(logger); + } + + if (cachedAnswers == null || lastPrefixSet != pathPrefixSet + || lastPrefixSet.getModCount() != pathPrefixSet.getModCount()) { + cachedAnswers = computeApplicableResources(logger, pathPrefixSet); + } + + return cachedAnswers; + } + + public JarFile getJarFile() { + return jarFile; + } + + @Override + public String getLocation() { + return new File(jarFile.getName()).toURI().toString(); + } + + private Set<JarFileResource> buildIndex(TreeLogger logger) { + logger = Messages.BUILDING_INDEX.branch(logger, jarFile.getName(), null); + + HashSet<JarFileResource> results = new HashSet<JarFileResource>(); + Enumeration<JarEntry> e = jarFile.entries(); + while (e.hasMoreElements()) { + JarEntry jarEntry = e.nextElement(); + if (jarEntry.isDirectory()) { + // Skip directories. + continue; + } + if (jarEntry.getName().startsWith("META-INF/")) { + // Skip META-INF since classloaders normally make this invisible. + continue; + } + JarFileResource jarResource = new JarFileResource(this, jarEntry); + results.add(jarResource); + Messages.READ_JAR_ENTRY.log(logger, jarEntry.getName(), null); + } + return results; + } + + private Set<AbstractResource> computeApplicableResources(TreeLogger logger, + PathPrefixSet pathPrefixSet) { + logger = Messages.FINDING_INCLUDED_RESOURCES.branch(logger, + jarFile.getName(), null); + + Set<AbstractResource> results = new HashSet<AbstractResource>(); + for (JarFileResource r : allJarFileResources) { + String path = r.getPath(); + if (pathPrefixSet.includesResource(path)) { + Messages.INCLUDING_RESOURCE.log(logger, path, null); + results.add(r); + } else { + Messages.EXCLUDING_RESOURCE.log(logger, path, null); + } + } + return results; + } +}
diff --git a/dev/core/src/com/google/gwt/dev/resource/impl/JarFileResource.java b/dev/core/src/com/google/gwt/dev/resource/impl/JarFileResource.java new file mode 100644 index 0000000..650d053 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/resource/impl/JarFileResource.java
@@ -0,0 +1,83 @@ +/* + * Copyright 2008 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.resource.impl; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.jar.JarEntry; + +/** + * Represents a resource contained in a jar file. + */ +public class JarFileResource extends AbstractResource { + + private final JarFileClassPathEntry classPathEntry; + private final JarEntry jarEntry; + + public JarFileResource(JarFileClassPathEntry classPathEntry, JarEntry jarEntry) { + this.classPathEntry = classPathEntry; + this.jarEntry = jarEntry; + } + + @Override + public JarFileClassPathEntry getClassPathEntry() { + return classPathEntry; + } + + public JarEntry getJarEntry() { + return jarEntry; + } + + @Override + public String getLocation() { + return "jar:" + classPathEntry.getLocation() + "!/" + getPath(); + } + + @Override + public String getPath() { + return jarEntry.getName(); + } + + @Override + public URL getURL() { + try { + return new URL(getLocation()); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + + /** + * Since we don't dynamically reload jars during a run, jar-based resources + * cannot become stale. + */ + @Override + public boolean isStale() { + return false; + } + + @Override + public InputStream openContents() { + try { + return classPathEntry.getJarFile().getInputStream(jarEntry); + } catch (IOException e) { + // The spec for this method says it can return null. + return null; + } + } +}
diff --git a/dev/core/src/com/google/gwt/dev/resource/impl/PathPrefix.java b/dev/core/src/com/google/gwt/dev/resource/impl/PathPrefix.java new file mode 100644 index 0000000..25537bb --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/resource/impl/PathPrefix.java
@@ -0,0 +1,142 @@ +/* + * Copyright 2008 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.resource.impl; + +/** + * Represents the abstract path prefix that goes between the + * {@link ClassPathEntry} and the rest of resource's abstract path. This concept + * allows us to specify subsets of path hierarchies orthogonally from path + * roots. For example, a path root might be <code>/home/gwt/src/</code> and an + * abstract path prefix might be <code>module/client/</code>. Importantly, + * you can apply the same abstract path prefix to multiple path roots and find + * more than one set of resources residing in disjoint locations yet occupying + * the same logical hierarchy. Sorry this explanation is so abstract; it's how + * we model things like the GWT module's client source path, public path, and + * super source path. + */ +public final class PathPrefix { + + public static final PathPrefix ALL = new PathPrefix("", null); + + private final ResourceFilter filter; + private final String prefix; + private final boolean shouldReroot; + + /** + * Construct a non-rerooting prefix. + * + * @param prefix a string prefix that (1) is the empty string or (2) begins + * with something other than a slash and ends with a slash + * @param filter the resource filter to use, or <code>null</code> for no + * filter; note that the filter must always return the same answer + * for the same candidate path (doing otherwise will produce + * inconsistent behavior in identifying available resources) + */ + public PathPrefix(String prefix, ResourceFilter filter) { + this(prefix, filter, false); + } + + /** + * Construct a prefix. + * + * @param prefix a string prefix that (1) is the empty string or (2) begins + * with something other than a slash and ends with a slash + * @param filter the resource filter to use, or <code>null</code> for no + * filter; note that the filter must always return the same answer + * for the same candidate path (doing otherwise will produce + * inconsistent behavior in identifying available resources) + * @param shouldReroot if <code>true</code>, any matching {@link Resource} + * for this prefix will be rerooted to not include the initial prefix + * path; if <code>false</code>, the prefix will be included in a + * matching resource's path. + * + */ + public PathPrefix(String prefix, ResourceFilter filter, boolean shouldReroot) { + assertValidPrefix(prefix); + this.prefix = prefix; + this.filter = filter; + this.shouldReroot = shouldReroot; + } + + /** + * Determines whether or not a given path is allowed by this path prefix by + * checking both the prefix string and the filter. + * + * @param path + * @return + */ + public boolean allows(String path) { + if (!path.startsWith(prefix)) { + return false; + } + if (filter == null) { + return true; + } + if (shouldReroot) { + path = getRerootedPath(path); + } + return filter.allows(path); + } + + /** + * Equality is based on prefixes representing the same string. Importantly, + * the filter does not affect equality. + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof PathPrefix) { + if (prefix.equals(((PathPrefix) obj).prefix)) { + return true; + } + } + return false; + } + + /** + * The prefix. + * + * @return the result is guaranteed to be non-<code>null</code>, and + * either be the empty string or it will not begin with a slash and + * will end with a slash; these guarantees are very useful when + * concatenating paths that incorporate prefixes + */ + public String getPrefix() { + return prefix; + } + + public String getRerootedPath(String path) { + assert (path.startsWith(prefix)); + if (shouldReroot) { + return path.substring(prefix.length()); + } else { + return path; + } + } + + @Override + public int hashCode() { + return prefix.hashCode(); + } + + public boolean shouldReroot() { + return shouldReroot; + } + + private void assertValidPrefix(String prefix) { + assert (prefix != null); + assert ("".equals(prefix) || (!prefix.startsWith("/") && prefix.endsWith("/"))) : "malformed prefix"; + } +}
diff --git a/dev/core/src/com/google/gwt/dev/resource/impl/PathPrefixSet.java b/dev/core/src/com/google/gwt/dev/resource/impl/PathPrefixSet.java new file mode 100644 index 0000000..08a7832 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/resource/impl/PathPrefixSet.java
@@ -0,0 +1,270 @@ +/* + * Copyright 2008 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.resource.impl; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Combines the information conveyed about a set of path prefixes to quickly + * answer questions regarding an entire set of path prefixes. + */ +public class PathPrefixSet { + + private static class TrieNode { + // TODO(bruce): test to see if Map would be faster; I'm on the fence + private final List<TrieNode> children = new ArrayList<TrieNode>(); + private final String part; + private PathPrefix prefix; + + public TrieNode(String part) { + this.part = part; + } + + public TrieNode addChild(String part) { + assert (findChild(part) == null); + TrieNode newChild = new TrieNode(part); + children.add(newChild); + return newChild; + } + + public TrieNode findChild(String part) { + for (TrieNode child : children) { + if (child.part.equals(part)) { + return child; + } + } + return null; + } + + public PathPrefix getPathPrefix() { + return prefix; + } + + public boolean hasChildren() { + return !children.isEmpty(); + } + + public void setPathPrefix(PathPrefix prefix) { + this.prefix = prefix; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + toString(sb, ""); + return sb.toString(); + } + + private void toString(StringBuilder sb, String indent) { + if (sb.length() > 0) { + sb.append('\n'); + } + sb.append(indent); + sb.append(' '); + sb.append(part); + for (TrieNode child : children) { + child.toString(sb, indent + " "); + } + } + } + + private int modCount; + private final Map<String, PathPrefix> prefixes = new HashMap<String, PathPrefix>(); + private final TrieNode rootTrieNode = new TrieNode("/"); + + /** + * @param prefix the prefix to add + * @return <code>true</code> if the prefix was not already in the set; + * otherwise, it replaced an identical one having the same prefix, + * which has the effect of changing which filter is used (last one + * wins) + */ + public boolean add(PathPrefix prefix) { + ++modCount; + String pathPrefix = prefix.getPrefix(); + prefixes.put(pathPrefix, prefix); + + /* + * An empty prefix means we have no prefix requirement, but we do attached + * the prefix to the root so that we can apply the filter. + */ + if ("".equals(pathPrefix)) { + rootTrieNode.setPathPrefix(prefix); + return false; + } + + // TODO(bruce): consider not using split for speed + String[] parts = pathPrefix.split("/"); + TrieNode parentNode = rootTrieNode; + boolean didAdd = false; + for (String part : parts) { + TrieNode childNode = parentNode.findChild(part); + if (childNode != null) { + // Follow existing branch. + parentNode = childNode; + } else { + // Add a new branch. + parentNode = parentNode.addChild(part); + didAdd = true; + } + } + assert (parentNode != null); + // This may clobber an existing one, but that's okay. Last one wins. + parentNode.setPathPrefix(prefix); + return didAdd; + } + + public int getModCount() { + return modCount; + } + + /** + * Determines whether or not a directory might have resources that could be + * included. The primary purpose of this method is to allow + * {@link ClassPathEntry} subclasses to avoid descending into directory + * hierarchies that could not possibly contain resources that would be + * included by {@link #includesResource(String). + * + * @param dirPath must be a valid abstract directory name or the empty string + * @return + */ + public boolean includesDirectory(String dirPath) { + assertValidAbstractDirectoryPathName(dirPath); + + /* + * There are five cases: + * + * (0) dirPath is the empty string, which is (a) trivially included unless + * (b) no prefix paths have been specified at all. + * + * (1) The empty string was specified as a prefix, which causes everything + * to be included. + * + * (2) As we walk the parts of dirPath, we see a path prefix attached to one + * of the trie nodes we encounter. This means that there was a specified + * prefix that this dirPath falls underneath, so it is included. + * + * (3) dirPath is longer than the trie, but we never encounter a path prefix + * as we walk the trie. This indicates that this directory doesn't fall into + * any of the specified prefixes. + * + * (4) dirPath is not longer than the trie and stays on the trie the whole + * time, which means it is included (since at least some longer prefix + * includes it). + */ + + // if ("".equals(dirPath)) { + // if (rootTrieNode.hasChildren() || rootTrieNode.getPathPrefix() != null) { + // // Case (0)(a): trivially true. + // return true; + // } else { + // // Case (0)(b): no directories are included. + // return false; + // } + // } + if (rootTrieNode.getPathPrefix() != null) { + // Case (1). + return true; + } + + TrieNode parentNode = rootTrieNode; + + String[] parts = dirPath.split("/"); + for (String part : parts) { + assert (!"".equals(part)); + TrieNode childNode = parentNode.findChild(part); + if (childNode != null) { + PathPrefix pathPrefix = childNode.getPathPrefix(); + if (pathPrefix != null) { + // Case (2). + return true; + } + + // Haven't found a path prefix yet, so keep walking. + parentNode = childNode; + } else { + // Case (3). + return false; + } + } + + // Case (4). + return true; + } + + /** + * Determines whether or not a given resource should be allowed by this path + * prefix set and the corresponding filters. + * + * @param resourceAbstractPathName + * @return <code>true</code> if the resource matches some specified prefix + * and any associated filters don't exclude it + */ + public boolean includesResource(String resourceAbstractPathName) { + assertValidAbstractResourcePathName(resourceAbstractPathName); + + TrieNode parentNode = rootTrieNode; + PathPrefix matchingPrefix = rootTrieNode.getPathPrefix(); + + // TODO(bruce): consider not using split for speed + String[] parts = resourceAbstractPathName.split("/"); + + // Walk all but the last path part, which is assumed to be a file name. + for (int i = 0, n = parts.length - 1; i < n; ++i) { + String part = parts[i]; + assert (!"".equals(part)); + TrieNode childNode = parentNode.findChild(part); + if (childNode != null) { + // Follow valid branch. + matchingPrefix = childNode.getPathPrefix(); + parentNode = childNode; + } else { + // No valid branch to follow. + break; + } + } + + if (matchingPrefix == null) { + // Didn't match any specified prefix. + return false; + } + + // It did match a prefix, but we still need to test. + return matchingPrefix.allows(resourceAbstractPathName); + } + + public Collection<PathPrefix> values() { + return Collections.unmodifiableCollection(prefixes.values()); + } + + private void assertValidAbstractDirectoryPathName(String name) { + assert (name != null); + // assert ("".equals(name) || (!name.startsWith("/") && + // name.endsWith("/"))); + assert (!name.startsWith("/") && name.endsWith("/")); + } + + private void assertValidAbstractResourcePathName(String name) { + assert (name != null); + assert (!"".equals(name)); + assert (!name.startsWith("/") && !name.endsWith("/")); + } +}
diff --git a/dev/core/src/com/google/gwt/dev/resource/impl/ResourceFilter.java b/dev/core/src/com/google/gwt/dev/resource/impl/ResourceFilter.java new file mode 100644 index 0000000..51cbfdc --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/resource/impl/ResourceFilter.java
@@ -0,0 +1,28 @@ +/* + * Copyright 2008 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.resource.impl; + +/** + * Used to decide whether or not a resource name should be included. + */ +public interface ResourceFilter { + + /** + * Determines if the specified path is included by the filter. + */ + boolean allows(String path); + +}
diff --git a/dev/core/src/com/google/gwt/dev/resource/impl/ResourceOracleImpl.java b/dev/core/src/com/google/gwt/dev/resource/impl/ResourceOracleImpl.java new file mode 100644 index 0000000..0d429ff --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/resource/impl/ResourceOracleImpl.java
@@ -0,0 +1,366 @@ +/* + * Copyright 2008 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.resource.impl; + +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.dev.resource.Resource; +import com.google.gwt.dev.resource.ResourceOracle; +import com.google.gwt.dev.util.msg.Message0; +import com.google.gwt.dev.util.msg.Message1String; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.jar.JarFile; + +/** + * The normal implementation of {@link ResourceOracle}. + */ +public class ResourceOracleImpl implements ResourceOracle { + + private static class Messages { + static final Message1String EXAMINING_PATH_ROOT = new Message1String( + TreeLogger.DEBUG, "Searching for resources within $0"); + + static final Message1String IGNORING_SHADOWED_RESOURCE = new Message1String( + TreeLogger.DEBUG, + "Resource '$0' is being shadowed by another resource higher in the classpath having the same name; this one will not be used"); + + static final Message1String NEW_RESOURCE_FOUND = new Message1String( + TreeLogger.TRACE, "Found new resource: $0"); + + static final Message0 NO_RESOURCES_CHANGED = new Message0(TreeLogger.DEBUG, + "No resources changed"); + + static final Message0 REFRESHING_RESOURCES = new Message0(TreeLogger.TRACE, + "Refreshing resources"); + + static final Message1String RESOURCE_BECAME_INVALID_BECAUSE_IT_IS_STALE = new Message1String( + TreeLogger.SPAM, + "Resource '$0' has been modified since it was last loaded and needs to be reloaded"); + + static final Message1String RESOURCE_BECAME_INVALID_BECAUSE_IT_MOVED = new Message1String( + TreeLogger.DEBUG, + "Resource '$0' was found on a different classpath entry and needs to be reloaded"); + } + + /** + * Used by rebasing {@link ResourceOracle ResourceOracles} to map from a full + * classpath-based abstract path to an abstract path within a logical package. + * + * @see ResourceOracleImpl#shouldRebasePaths() + */ + private static class ResourceWrapper extends AbstractResource { + private final String path; + private final AbstractResource resource; + + public ResourceWrapper(String path, AbstractResource resource) { + this.path = path; + this.resource = resource; + } + + @Override + public ClassPathEntry getClassPathEntry() { + return resource.getClassPathEntry(); + } + + @Override + public String getLocation() { + return resource.getLocation(); + } + + @Override + public String getPath() { + return path; + } + + @Override + public URL getURL() { + return resource.getURL(); + } + + @Override + public boolean isStale() { + return resource.isStale(); + } + + @Override + public InputStream openContents() { + return resource.openContents(); + } + } + + public static ClassPathEntry createEntryForUrl(TreeLogger logger, URL url) + throws URISyntaxException, IOException { + String urlString = url.toString(); + if (url.getProtocol().equals("file")) { + URI uri = new URI(urlString); + File f = new File(uri); + if (f.isDirectory()) { + return new DirectoryClassPathEntry(f); + } else if (f.isFile() && f.getName().endsWith(".jar")) { + return new JarFileClassPathEntry(new JarFile(f)); + } else { + logger.log(TreeLogger.WARN, "Unexpected error reading classpath; " + f + + " is neither a directory nor a jar"); + return null; + } + } else { + logger.log(TreeLogger.WARN, "Unknown URL type for " + urlString, null); + return null; + } + } + + private static void addAllClassPathEntries(TreeLogger logger, + ClassLoader classLoader, List<ClassPathEntry> classPath) { + for (; classLoader != null; classLoader = classLoader.getParent()) { + if (classLoader instanceof URLClassLoader) { + URLClassLoader urlClassLoader = (URLClassLoader) classLoader; + URL[] urls = urlClassLoader.getURLs(); + for (URL url : urls) { + Throwable caught; + try { + ClassPathEntry entry = createEntryForUrl(logger, url); + if (entry != null) { + classPath.add(entry); + } + continue; + } catch (URISyntaxException e) { + caught = e; + } catch (IOException e) { + caught = e; + } + logger.log(TreeLogger.WARN, "Error processing classpath URL '" + url + + "'", caught); + } + } + } + } + + private static List<ClassPathEntry> getAllClassPathEntries(TreeLogger logger, + ClassLoader classLoader) { + ArrayList<ClassPathEntry> classPath = new ArrayList<ClassPathEntry>(); + addAllClassPathEntries(logger, classLoader, classPath); + return classPath; + } + + private final List<ClassPathEntry> classPath = new ArrayList<ClassPathEntry>(); + + private Set<String> exposedPathNames = Collections.emptySet(); + + private Map<String, Resource> exposedResourceMap = Collections.emptyMap(); + + private Set<Resource> exposedResources = Collections.emptySet(); + + private Map<String, AbstractResource> internalMap = Collections.emptyMap(); + + private PathPrefixSet pathPrefixSet = new PathPrefixSet(); + + /** + * Constructs a {@link ResourceOracleImpl} from a set of + * {@link ClassPathEntry ClassPathEntries}. The passed-in list is copied, but + * the underlying entries in the list are not. Those entries must be + * effectively immutable except for reflecting actual changes to the + * underlying resources. + */ + public ResourceOracleImpl(List<ClassPathEntry> classPath) { + this.classPath.addAll(classPath); + } + + /** + * Constructs a {@link ResourceOracleImpl} from the thread's default + * {@link ClassLoader}. + */ + public ResourceOracleImpl(TreeLogger logger) { + this(logger, Thread.currentThread().getContextClassLoader()); + } + + /** + * Constructs a {@link ResourceOracleImpl} from a {@link ClassLoader}. The + * specified {@link ClassLoader} and all of its parents which are instances of + * {@link URLClassLoader} will have their class path entries added to this + * instances underlying class path. + */ + public ResourceOracleImpl(TreeLogger logger, ClassLoader classLoader) { + this(getAllClassPathEntries(logger, classLoader)); + } + + public Set<String> getPathNames() { + return exposedPathNames; + } + + public Map<String, Resource> getResourceMap() { + return exposedResourceMap; + } + + public Set<Resource> getResources() { + return exposedResources; + } + + /** + * Rescans the associated paths to recompute the available resources. + * + * @param logger status and error details are written here + * @throws UnableToCompleteException + */ + public void refresh(TreeLogger logger) { + TreeLogger refreshBranch = Messages.REFRESHING_RESOURCES.branch(logger, + null); + + /* + * Allocate fresh data structures in anticipation of needing to honor the + * "new identity for the collections if anything changes" guarantee. + */ + final Map<String, AbstractResource> newInternalMap = new HashMap<String, AbstractResource>(); + + /* + * Walk across path roots (i.e. classpath entries) in priority order. This + * is a "reverse painter's algorithm", relying on being careful never to add + * a resource that has already been added to the new map under construction + * to create the effect that resources founder earlier on the classpath take + * precedence. + */ + int changeCount = 0; + for (ClassPathEntry pathRoot : classPath) { + TreeLogger branchForClassPathEntry = Messages.EXAMINING_PATH_ROOT.branch( + refreshBranch, pathRoot.getLocation(), null); + + int prevChangeCount = changeCount; + + Set<AbstractResource> newResources = pathRoot.findApplicableResources( + branchForClassPathEntry, pathPrefixSet); + for (AbstractResource newResource : newResources) { + String resourcePath = newResource.getPath(); + + // Make sure we don't already have a resource by this name. + if (newInternalMap.containsKey(resourcePath)) { + Messages.IGNORING_SHADOWED_RESOURCE.log(branchForClassPathEntry, + resourcePath, null); + continue; + } + + AbstractResource oldResource = internalMap.get(resourcePath); + if (shouldUseNewResource(branchForClassPathEntry, oldResource, + newResource)) { + newInternalMap.put(resourcePath, newResource); + ++changeCount; + } else if (oldResource != null) { + // Nothing changed, so carry the identity of the old one forward. + newInternalMap.put(resourcePath, oldResource); + } + } + + if (changeCount == prevChangeCount) { + Messages.NO_RESOURCES_CHANGED.log(branchForClassPathEntry, null); + } + } + + if (changeCount == 0) { + /* + * Nothing was added or modified, but we still have to be sure we didn't + * lose any resources. + */ + if (newInternalMap.size() == internalMap.size()) { + /* + * Exit without changing the current exposed collections to maintain the + * identity requirements described in the spec for ResourceOracle. + */ + return; + } + } + + internalMap = newInternalMap; + Map<String, Resource> externalMap = rerootResourcePaths(newInternalMap); + + // Create a constant-time set for resources. + Set<Resource> newResources = new HashSet<Resource>(externalMap.values()); + assert (newResources.size() == externalMap.size()); + + // Update the gettable fields with the new (unmodifiable) data structures. + exposedResources = Collections.unmodifiableSet(newResources); + exposedResourceMap = Collections.unmodifiableMap(externalMap); + exposedPathNames = Collections.unmodifiableSet(externalMap.keySet()); + } + + public void setPathPrefixes(PathPrefixSet pathPrefixSet) { + this.pathPrefixSet = pathPrefixSet; + } + + private Map<String, Resource> rerootResourcePaths( + Map<String, AbstractResource> newInternalMap) { + Map<String, Resource> externalMap; + // Create an external map with rebased path names. + externalMap = new HashMap<String, Resource>(); + for (AbstractResource resource : newInternalMap.values()) { + String path = resource.getPath(); + if (externalMap.get(path) instanceof ResourceWrapper) { + // A rerooted resource blocks any other resource at this path. + continue; + } + for (PathPrefix pathPrefix : pathPrefixSet.values()) { + if (pathPrefix.allows(path)) { + assert (path.startsWith(pathPrefix.getPrefix())); + if (pathPrefix.shouldReroot()) { + path = pathPrefix.getRerootedPath(path); + AbstractResource wrapper = new ResourceWrapper(path, resource); + externalMap.put(path, wrapper); + } else { + externalMap.put(path, resource); + } + break; + } + } + assert (externalMap.containsKey(path)); + } + return externalMap; + } + + private boolean shouldUseNewResource(TreeLogger logger, + AbstractResource oldResource, AbstractResource newResource) { + String resourcePath = newResource.getPath(); + if (oldResource != null) { + // Test 1: Is the resource found in a different location than before? + if (oldResource.getClassPathEntry() == newResource.getClassPathEntry()) { + // Test 2: Has the resource changed since we last found it? + if (!oldResource.isStale()) { + // The resource has not changed. + return false; + } else { + Messages.RESOURCE_BECAME_INVALID_BECAUSE_IT_IS_STALE.log(logger, + resourcePath, null); + } + } else { + Messages.RESOURCE_BECAME_INVALID_BECAUSE_IT_MOVED.log(logger, + resourcePath, null); + } + } else { + Messages.NEW_RESOURCE_FOUND.log(logger, resourcePath, null); + } + + return true; + } +}
diff --git a/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java b/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java index 2359e09..9d083a6 100644 --- a/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java +++ b/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
@@ -15,17 +15,19 @@ */ package com.google.gwt.dev.shell; +import com.google.gwt.core.client.GWTBridge; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.JMethod; import com.google.gwt.core.ext.typeinfo.JParameter; import com.google.gwt.core.ext.typeinfo.TypeOracle; -import com.google.gwt.dev.jdt.ByteCodeCompiler; -import com.google.gwt.dev.jdt.CacheManager; -import com.google.gwt.dev.shell.JsniMethods.JsniMethod; +import com.google.gwt.dev.javac.CompilationState; +import com.google.gwt.dev.javac.CompiledClass; +import com.google.gwt.dev.javac.JsniMethod; import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter; import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.InstanceMethodOracle; +import com.google.gwt.dev.util.Jsni; import com.google.gwt.dev.util.JsniRef; import com.google.gwt.util.tools.Utility; @@ -45,7 +47,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; @@ -342,12 +343,17 @@ * space (thus, they bridge across the spaces). */ private static final Class<?>[] BRIDGE_CLASSES = new Class<?>[] { - ShellJavaScriptHost.class, JsniMethods.class, JsniMethod.class}; + ShellJavaScriptHost.class, GWTBridge.class}; private static final boolean CLASS_DUMP = false; private static final String CLASS_DUMP_PATH = "rewritten-classes"; + /** + * Caches the byte code for {@link JavaScriptHost}. + */ + private static byte[] javaScriptHostBytes; + static { for (Class<?> c : BRIDGE_CLASSES) { BRIDGE_CLASS_NAMES.put(c.getName(), c); @@ -381,31 +387,65 @@ } } + /** + * Magic: {@link JavaScriptHost} was never compiled because it's a part of the + * hosted mode infrastructure. However, unlike {@link #BRIDGE_CLASSES}, + * {@code JavaScriptHost} needs a separate copy per inside the ClassLoader for + * each module. + */ + private static void ensureJavaScriptHostBytes(TreeLogger logger) + throws UnableToCompleteException { + + if (javaScriptHostBytes != null) { + return; + } + + String className = JavaScriptHost.class.getName(); + try { + String path = className.replace('.', '/') + ".class"; + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + URL url = cl.getResource(path); + if (url != null) { + javaScriptHostBytes = getClassBytesFromStream(url.openStream()); + } else { + logger.log(TreeLogger.ERROR, + "Could not find required bootstrap class '" + className + + "' in the classpath", null); + throw new UnableToCompleteException(); + } + } catch (IOException e) { + logger.log(TreeLogger.ERROR, + "Error reading class bytes for " + className, e); + throw new UnableToCompleteException(); + } + } + + private static byte[] getClassBytesFromStream(InputStream is) + throws IOException { + try { + byte classBytes[] = new byte[is.available()]; + int read = 0; + while (read < classBytes.length) { + read += is.read(classBytes, read, classBytes.length - read); + } + return classBytes; + } finally { + Utility.close(is); + } + } + private final HostedModeClassRewriter classRewriter; - private final ByteCodeCompiler compiler; + private CompilationState compilationState; private final DispatchClassInfoOracle dispClassInfoOracle = new DispatchClassInfoOracle(); - private Class<?> javaScriptHostClass; + private Class<?> gwtClass, javaScriptHostClass; private final TreeLogger logger; - /** - * Stores a list of classes needing JSNI injection. This list will be cleared - * when the {@link #stackDepth} is <code>0</code>. - */ - private final List<Class<?>> pendingJsniInjectionClasses = new ArrayList<Class<?>>(); - private ShellJavaScriptHost shellJavaScriptHost; - /** - * Used to guard against {@link ClassCircularityError}. Attempting to read - * class annotations for the purpose of JSNI injection while defining a class - * can lead to circularities; we must wait until we're at the "top of stack". - */ - private int stackDepth = 0; - private final TypeOracle typeOracle; @SuppressWarnings("unchecked") @@ -416,48 +456,19 @@ private final Map<Integer, Object> weakJsoCache = new ReferenceMap( AbstractReferenceMap.HARD, AbstractReferenceMap.WEAK); - public CompilingClassLoader(TreeLogger logger, ByteCodeCompiler compiler, - TypeOracle typeOracle, ShellJavaScriptHost javaScriptHost) + public CompilingClassLoader(TreeLogger logger, + CompilationState compilationState, ShellJavaScriptHost javaScriptHost) throws UnableToCompleteException { super(null); this.logger = logger; - this.compiler = compiler; - this.typeOracle = typeOracle; + this.compilationState = compilationState; this.shellJavaScriptHost = javaScriptHost; + this.typeOracle = compilationState.getTypeOracle(); // Assertions are always on in hosted mode. setDefaultAssertionStatus(true); - // SPECIAL MAGIC: Prevents the compile process from ever trying to compile - // these guys from source, which is what we want, since they are special and - // neither of them would compile correctly from source. - // - // JavaScriptHost is special because its type cannot be known to the user. - // It is referenced only from generated code and GWT.create. - // - for (Class<?> clazz : CacheManager.BOOTSTRAP_CLASSES) { - String className = clazz.getName(); - try { - String path = clazz.getName().replace('.', '/').concat(".class"); - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - URL url = cl.getResource(path); - if (url != null) { - byte classBytes[] = getClassBytesFromStream(url.openStream()); - String loc = url.toExternalForm(); - compiler.putClassBytes(logger, className, classBytes, loc); - } else { - logger.log(TreeLogger.ERROR, - "Could not find required bootstrap class '" + className - + "' in the classpath", null); - throw new UnableToCompleteException(); - } - } catch (IOException e) { - logger.log(TreeLogger.ERROR, "Error reading class bytes for " - + className, e); - throw new UnableToCompleteException(); - } - } - compiler.removeStaleByteCode(logger); + ensureJavaScriptHostBytes(logger); // Create a class rewriter based on all the subtypes of the JSO class. JClassType jsoType = typeOracle.findType(JsValueGlue.JSO_CLASS); @@ -574,82 +585,22 @@ } // Get the bytes, compiling if necessary. - byte[] classBytes; - try { - ++stackDepth; - if (classRewriter != null && classRewriter.isJsoIntf(className)) { - // Generate a synthetic JSO interface class. - classBytes = classRewriter.writeJsoIntf(className); - } else { - // A JSO impl class needs the class bytes for the original class. - String lookupClassName = className; - if (classRewriter != null && classRewriter.isJsoImpl(className)) { - lookupClassName = className.substring(0, className.length() - 1); - } - classBytes = compiler.getClassBytes(logger, lookupClassName); - if (classRewriter != null) { - byte[] newBytes = classRewriter.rewrite(className, classBytes); - if (CLASS_DUMP) { - if (!Arrays.equals(classBytes, newBytes)) { - classDump(className, classBytes); - } - } - classBytes = newBytes; - } - } - Class<?> newClass = defineClass(className, classBytes, 0, - classBytes.length); - - if (className.equals(JavaScriptHost.class.getName())) { - javaScriptHostClass = newClass; - updateJavaScriptHost(); - } - - return newClass; - } catch (UnableToCompleteException e) { + byte[] classBytes = findClassBytes(className); + if (classBytes == null) { throw new ClassNotFoundException(className); - } finally { - --stackDepth; - } - } - - /** - * Overridden to process JSNI annotations. - */ - @Override - protected synchronized Class<?> loadClass(String name, boolean resolve) - throws ClassNotFoundException { - Class<?> newClass = super.loadClass(name, resolve); - - // Only real, non-local classes can have JSNI method annotations. - if (!newClass.isInterface() && !newClass.isLocalClass()) { - pendingJsniInjectionClasses.add(newClass); } - if (stackDepth == 0 && !pendingJsniInjectionClasses.isEmpty()) { - // Save a copy because this can re-enter. - Class<?>[] toCheck = pendingJsniInjectionClasses.toArray(new Class<?>[pendingJsniInjectionClasses.size()]); - pendingJsniInjectionClasses.clear(); - for (Class<?> checkClass : toCheck) { - JsniMethods jsniMethods = checkClass.getAnnotation(JsniMethods.class); - if (jsniMethods != null) { - for (JsniMethod jsniMethod : jsniMethods.value()) { - String[] bodyParts = jsniMethod.body(); - int size = 0; - for (String bodyPart : bodyParts) { - size += bodyPart.length(); - } - StringBuilder body = new StringBuilder(size); - for (String bodyPart : bodyParts) { - body.append(bodyPart); - } - shellJavaScriptHost.createNative(jsniMethod.file(), - jsniMethod.line(), jsniMethod.name(), jsniMethod.paramNames(), - body.toString()); - } - } - } + Class<?> newClass = defineClass(className, classBytes, 0, classBytes.length); + if (className.equals(JavaScriptHost.class.getName())) { + javaScriptHostClass = newClass; + updateJavaScriptHost(); } + + if (className.equals("com.google.gwt.core.client.GWT")) { + gwtClass = newClass; + updateGwtClass(); + } + return newClass; } @@ -662,22 +613,59 @@ dispClassInfoOracle.clear(); } + private byte[] findClassBytes(String className) { + if (JavaScriptHost.class.getName().equals(className)) { + // No need to rewrite. + return javaScriptHostBytes; + } + + if (classRewriter != null && classRewriter.isJsoIntf(className)) { + // Generate a synthetic JSO interface class. + return classRewriter.writeJsoIntf(className); + } + + // A JSO impl class needs the class bytes for the original class. + String lookupClassName = className.replace('.', '/'); + if (classRewriter != null && classRewriter.isJsoImpl(className)) { + lookupClassName = lookupClassName.substring(0, + lookupClassName.length() - 1); + } + + CompiledClass compiledClass = compilationState.getClassFileMap().get( + lookupClassName); + if (compiledClass != null) { + injectJsniFor(compiledClass); + + byte[] classBytes = compiledClass.getBytes(); + if (classRewriter != null) { + byte[] newBytes = classRewriter.rewrite(className, classBytes); + if (CLASS_DUMP) { + if (!Arrays.equals(classBytes, newBytes)) { + classDump(className, newBytes); + } + } + classBytes = newBytes; + } + return classBytes; + } + return null; + } + private String getBinaryName(JClassType type) { String name = type.getPackage().getName() + '.'; name += type.getName().replace('.', '$'); return name; } - private byte[] getClassBytesFromStream(InputStream is) throws IOException { - try { - byte classBytes[] = new byte[is.available()]; - int read = 0; - while (read < classBytes.length) { - read += is.read(classBytes, read, classBytes.length - read); + private void injectJsniFor(CompiledClass compiledClass) { + for (JsniMethod jsniMethod : compiledClass.getJsniMethods()) { + String body = Jsni.getJavaScriptForHostedMode(logger, jsniMethod); + if (body == null) { + // The error has been logged; just ignore it for now. + continue; } - return classBytes; - } finally { - Utility.close(is); + shellJavaScriptHost.createNative(jsniMethod.location(), + jsniMethod.line(), jsniMethod.name(), jsniMethod.paramNames(), body); } } @@ -695,8 +683,47 @@ /** * Tricky one, this. Reaches over into this modules's JavaScriptHost class and - * sets its static 'host' field to be the specified ModuleSpace instance - * (which will either be this ModuleSpace or null). + * sets its static 'host' field to our module space. + * + * @param moduleSpace the ModuleSpace instance to store using + * JavaScriptHost.setHost(). + * @see JavaScriptHost + */ + private void updateGwtClass() { + if (gwtClass == null) { + return; + } + Throwable caught; + try { + GWTBridgeImpl bridge; + if (shellJavaScriptHost == null) { + bridge = null; + } else { + bridge = new GWTBridgeImpl(shellJavaScriptHost); + } + final Class<?>[] paramTypes = new Class[] {GWTBridge.class}; + Method setBridgeMethod = gwtClass.getDeclaredMethod("setBridge", + paramTypes); + setBridgeMethod.setAccessible(true); + setBridgeMethod.invoke(gwtClass, new Object[] {bridge}); + return; + } catch (SecurityException e) { + caught = e; + } catch (NoSuchMethodException e) { + caught = e; + } catch (IllegalArgumentException e) { + caught = e; + } catch (IllegalAccessException e) { + caught = e; + } catch (InvocationTargetException e) { + caught = e.getTargetException(); + } + throw new RuntimeException("Error initializing GWT bridge", caught); + } + + /** + * Tricky one, this. Reaches over into this modules's JavaScriptHost class and + * sets its static 'host' field to our module space. * * @param moduleSpace the ModuleSpace instance to store using * JavaScriptHost.setHost().
diff --git a/dev/core/src/com/google/gwt/dev/shell/GWTBridgeImpl.java b/dev/core/src/com/google/gwt/dev/shell/GWTBridgeImpl.java new file mode 100644 index 0000000..973145c --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/shell/GWTBridgeImpl.java
@@ -0,0 +1,57 @@ +/* + * Copyright 2008 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.shell; + +import com.google.gwt.core.client.GWTBridge; +import com.google.gwt.dev.About; + +/** + * This class is the hosted-mode peer for {@link com.google.gwt.core.client.GWT}. + */ +public class GWTBridgeImpl extends GWTBridge { + + private final ShellJavaScriptHost host; + + public GWTBridgeImpl(ShellJavaScriptHost host) { + this.host = host; + } + + /** + * Resolves a deferred binding request and create the requested object. + */ + public <T> T create(Class<?> requestedClass) { + String className = requestedClass.getName(); + try { + return host.<T> rebindAndCreate(className); + } catch (Throwable e) { + String msg = "Deferred binding failed for '" + className + + "' (did you forget to inherit a required module?)"; + throw new RuntimeException(msg, e); + } + }; + + public String getVersion() { + return About.GWT_VERSION_NUM; + } + + /** + * Logs in dev shell. + */ + public void log(String message, Throwable e) { + host.log(message, e); + } + +}
diff --git a/dev/core/src/com/google/gwt/dev/shell/GWTShellServlet.java b/dev/core/src/com/google/gwt/dev/shell/GWTShellServlet.java index 90f3a5a..bdbcd1b 100644 --- a/dev/core/src/com/google/gwt/dev/shell/GWTShellServlet.java +++ b/dev/core/src/com/google/gwt/dev/shell/GWTShellServlet.java
@@ -25,6 +25,7 @@ import com.google.gwt.dev.cfg.ModuleDef; import com.google.gwt.dev.cfg.ModuleDefLoader; import com.google.gwt.dev.jjs.JJSOptions; +import com.google.gwt.dev.resource.Resource; import com.google.gwt.dev.util.HttpHeaders; import com.google.gwt.dev.util.Util; import com.google.gwt.dev.util.log.ServletContextTreeLogger; @@ -410,12 +411,15 @@ return; } - URL foundResource; + URL foundResource = null; try { // Look for the requested file on the public path. // ModuleDef moduleDef = getModuleDef(logger, moduleName); - foundResource = moduleDef.findPublicFile(partialPath); + Resource publicResource = moduleDef.findPublicFile(partialPath); + if (publicResource != null) { + foundResource = publicResource.getURL(); + } if (foundResource == null) { // Look for generated files
diff --git a/dev/core/src/com/google/gwt/dev/shell/HostedModeServletContextProxy.java b/dev/core/src/com/google/gwt/dev/shell/HostedModeServletContextProxy.java index adf1ced..5270bf1 100644 --- a/dev/core/src/com/google/gwt/dev/shell/HostedModeServletContextProxy.java +++ b/dev/core/src/com/google/gwt/dev/shell/HostedModeServletContextProxy.java
@@ -17,6 +17,7 @@ import com.google.gwt.dev.GWTShell; import com.google.gwt.dev.cfg.ModuleDef; +import com.google.gwt.dev.resource.Resource; import java.io.File; import java.io.IOException; @@ -172,15 +173,17 @@ String partialPath = path.substring(moduleContext.length()); // Try to get the resource from the application's public path - URL url = moduleDef.findPublicFile(partialPath); - if (url == null) { - // Otherwise try the path but rooted in the shell's output directory - File shellDir = new File(outDir, GWTShell.GWT_SHELL_PATH + File.separator - + moduleDef.getName()); - File requestedFile = new File(shellDir, partialPath); - if (requestedFile.exists()) { - url = requestedFile.toURI().toURL(); - } + Resource publicResource = moduleDef.findPublicFile(partialPath); + if (publicResource != null) { + return publicResource.getURL(); + } + + // Otherwise try the path but rooted in the shell's output directory + File shellDir = new File(outDir, GWTShell.GWT_SHELL_PATH + File.separator + + moduleDef.getName()); + File requestedFile = new File(shellDir, partialPath); + if (requestedFile.exists()) { + return requestedFile.toURI().toURL(); } /* @@ -188,19 +191,16 @@ * directory for the file. We'll default to using the output directory of * the first linker defined in the <set-linker> tab. */ - if (url == null) { - File requestedFile = new File(new File(outDir, moduleDef.getName()), - partialPath); - if (requestedFile.exists()) { - try { - url = requestedFile.toURI().toURL(); - } catch (MalformedURLException e) { - // ignore since it was speculative anyway - } + requestedFile = new File(new File(outDir, moduleDef.getName()), partialPath); + if (requestedFile.exists()) { + try { + return requestedFile.toURI().toURL(); + } catch (MalformedURLException e) { + // ignore since it was speculative anyway } } - return url; + return null; } /**
diff --git a/dev/core/src/com/google/gwt/dev/shell/HostedModeSourceOracle.java b/dev/core/src/com/google/gwt/dev/shell/HostedModeSourceOracle.java deleted file mode 100644 index 64547c5..0000000 --- a/dev/core/src/com/google/gwt/dev/shell/HostedModeSourceOracle.java +++ /dev/null
@@ -1,75 +0,0 @@ -/* - * Copyright 2007 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.shell; - -import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider; -import com.google.gwt.core.ext.typeinfo.TypeOracle; -import com.google.gwt.dev.jdt.StandardSourceOracle; -import com.google.gwt.dev.jdt.StaticCompilationUnitProvider; -import com.google.gwt.util.tools.Utility; - -import java.io.File; -import java.io.IOException; - -/** - * Does a little extra magic to handle hosted mode JSNI and - * <code>GWT.create()</code>. - */ -public class HostedModeSourceOracle extends StandardSourceOracle { - - private final JsniInjector injector; - private final File jsniSaveDirectory; - - public HostedModeSourceOracle(TypeOracle typeOracle, File jsniSaveDirectory) { - super(typeOracle); - this.injector = new JsniInjector(typeOracle); - this.jsniSaveDirectory = jsniSaveDirectory; - } - - @Override - protected CompilationUnitProvider doFilterCompilationUnit(TreeLogger logger, - String typeName, CompilationUnitProvider existing) - throws UnableToCompleteException { - - /* - * MAGIC: The implementation of GWT can be very different between hosted - * mode and web mode. The compiler has special knowledge of GWT for web - * mode. The source for hosted mode is in GWT.java-hosted. - */ - if (typeName.equals("com.google.gwt.core.client.GWT")) { - try { - String source = Utility.getFileFromClassPath("com/google/gwt/core/client/GWT.java-hosted"); - return new StaticCompilationUnitProvider("com.google.gwt.core.client", - "GWT", source.toCharArray()); - } catch (IOException e) { - logger.log( - TreeLogger.ERROR, - "Unable to load 'com/google/gwt/core/client/GWT.java-hosted' from class path; is your installation corrupt?", - e); - throw new UnableToCompleteException(); - } - } - - // Otherwise, it's a regular translatable type, but we want to make sure - // its JSNI stuff, if any, gets handled. - // - CompilationUnitProvider jsnified = injector.inject(logger, existing, - jsniSaveDirectory); - return jsnified; - } -}
diff --git a/dev/core/src/com/google/gwt/dev/shell/JavaScriptHost.java b/dev/core/src/com/google/gwt/dev/shell/JavaScriptHost.java index b0cd9f8..17a1b5c 100644 --- a/dev/core/src/com/google/gwt/dev/shell/JavaScriptHost.java +++ b/dev/core/src/com/google/gwt/dev/shell/JavaScriptHost.java
@@ -108,27 +108,6 @@ } /** - * Logs in dev shell. - */ - public static void log(String message, Throwable e) { - sHost.log(message, e); - } - - /** - * Resolves a deferred binding request and create the requested object. - */ - public static <T> T rebindAndCreate(Class<?> requestedClass) { - String className = requestedClass.getName(); - try { - return sHost.<T> rebindAndCreate(className); - } catch (Throwable e) { - String msg = "Deferred binding failed for '" + className - + "' (did you forget to inherit a required module?)"; - throw new RuntimeException(msg, e); - } - } - - /** * This method is called via reflection from the {@link CompilingClassLoader}, * providing the hosted mode application with all of the methods it needs to * interface with the browser and the server (for deferred binding).
diff --git a/dev/core/src/com/google/gwt/dev/shell/JsniInjector.java b/dev/core/src/com/google/gwt/dev/shell/JsniInjector.java deleted file mode 100644 index 34d2917..0000000 --- a/dev/core/src/com/google/gwt/dev/shell/JsniInjector.java +++ /dev/null
@@ -1,304 +0,0 @@ -/* - * Copyright 2008 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.shell; - -import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider; -import com.google.gwt.core.ext.typeinfo.JClassType; -import com.google.gwt.core.ext.typeinfo.JMethod; -import com.google.gwt.core.ext.typeinfo.JParameter; -import com.google.gwt.core.ext.typeinfo.TypeOracle; -import com.google.gwt.dev.jdt.CompilationUnitProviderWithAlternateSource; -import com.google.gwt.dev.js.ast.JsBlock; -import com.google.gwt.dev.shell.JsniMethods.JsniMethod; -import com.google.gwt.dev.util.Jsni; -import com.google.gwt.dev.util.StringCopier; -import com.google.gwt.dev.util.Util; - -import java.io.File; -import java.lang.annotation.Annotation; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Map; - -/** - * Adapts compilation units containing JSNI-accessible code by rewriting the - * source. - */ -public class JsniInjector { - - /** - * A chunk of replacement text and where to put it. - */ - private static class Replacement implements Comparable<Replacement> { - public final int end; - - public final int start; - - public final char[] text; - - public Replacement(int start, int end, char[] text) { - this.start = start; - this.end = end; - this.text = text; - } - - public int compareTo(Replacement other) { - if (start < other.start) { - assert (end <= other.start) : "Overlapping changes not supported"; - return -1; - } else if (start > other.start) { - assert (start >= other.end) : "Overlapping changes not supported"; - return 1; - } else { - return 0; - } - } - } - - private static final int BLOCK_SIZE = 1024; - - private static final String JSNIMETHOD_NAME = JsniMethod.class.getName().replace( - '$', '.'); - - private static final String JSNIMETHODS_NAME = JsniMethods.class.getName(); - - private final Map<JClassType, List<JsniMethod>> jsniMethodMap = new IdentityHashMap<JClassType, List<JsniMethod>>(); - private final TypeOracle oracle; - - public JsniInjector(TypeOracle oracle) { - this.oracle = oracle; - } - - public CompilationUnitProvider inject(TreeLogger logger, - CompilationUnitProvider cup, File jsniSaveDirectory) - throws UnableToCompleteException { - - logger = logger.branch(TreeLogger.SPAM, - "Checking for JavaScript native methods", null); - - // Analyze the source and build a list of changes. - char[] source = cup.getSource(); - List<Replacement> changes = new ArrayList<Replacement>(); - rewriteCompilationUnit(logger, source, changes, cup, false); - - // Sort and apply the changes. - int n = changes.size(); - if (n > 0) { - Replacement[] repls = changes.toArray(new Replacement[n]); - Arrays.sort(repls); - StringCopier copier = new StringCopier(source); - for (int i = 0; i < n; ++i) { - Replacement repl = repls[i]; - copier.commit(repl.text, repl.start, repl.end); - } - - char[] results = copier.finish(); - - if (jsniSaveDirectory != null) { - String originalPath = cup.getLocation().replace(File.separatorChar, '/'); - String suffix = cup.getPackageName().replace('.', '/'); - int pos = originalPath.indexOf(suffix); - if (pos >= 0) { - String filePath = originalPath.substring(pos); - File out = new File(jsniSaveDirectory, filePath); - Util.writeCharsAsFile(logger, out, results); - } - } - - return new CompilationUnitProviderWithAlternateSource(cup, results); - } else { - // No changes were made, so we return the original. - logger.log(TreeLogger.SPAM, "No JavaScript native methods were found", - null); - return cup; - } - } - - private void collectJsniMethods(TreeLogger logger, char[] source, - JClassType type) throws UnableToCompleteException { - - // Locate the nearest non-local type; don't try to annotate local types. - JClassType targetType = type; - while (targetType.isLocalType()) { - targetType = targetType.getEnclosingType(); - } - List<JsniMethod> jsniMethods = jsniMethodMap.get(targetType); - String loc = type.getCompilationUnit().getLocation(); - - for (JMethod method : type.getMethods()) { - if (!method.isNative()) { - continue; - } - Jsni.Interval interval = Jsni.findJsniSource(method); - if (interval == null) { - String msg = "No JavaScript body found for native method '" + method - + "' in type '" + type + "'"; - logger.log(TreeLogger.ERROR, msg, null); - throw new UnableToCompleteException(); - } - // Parse it. - String js = String.valueOf(source, interval.start, interval.end - - interval.start); - int startLine = Jsni.countNewlines(source, 0, interval.start) + 1; - JsBlock body = Jsni.parseAsFunctionBody(logger, js, loc, startLine); - - // Add JsniMethod annotations to the target type. - if (jsniMethods == null) { - jsniMethods = new ArrayList<JsniMethod>(); - jsniMethodMap.put(targetType, jsniMethods); - } - jsniMethods.add(createJsniMethod(method, body, loc, source)); - } - } - - private JsniMethod createJsniMethod(JMethod method, JsBlock jsniBody, - final String file, char[] source) { - - final int line = Jsni.countNewlines(source, 0, method.getBodyStart()) + 1; - - final String name = Jsni.getJsniSignature(method); - - JParameter[] params = method.getParameters(); - final String[] paramNames = new String[params.length]; - for (int i = 0; i < params.length; ++i) { - paramNames[i] = params[i].getName(); - } - - /* - * Surround the original JS body statements with a try/catch so that we can - * map JavaScript exceptions back into Java. Note that the method body - * itself will print curly braces, so we don't need them around the - * try/catch. - */ - String jsTry = "try "; - String jsCatch = " catch (e) {\n __static[\"@" + Jsni.JAVASCRIPTHOST_NAME - + "::exceptionCaught(Ljava/lang/Object;)\"](e == null ? null : e);\n" - + "}\n"; - String body = jsTry + Jsni.generateJavaScriptForHostedMode(jsniBody) - + jsCatch; - - /* - * Break up the body into 1k strings; this ensures we don't blow up any - * class file limits. - */ - int length = body.length(); - final String[] bodyParts = new String[(length + BLOCK_SIZE - 1) - / BLOCK_SIZE]; - for (int i = 0; i < bodyParts.length; ++i) { - int startIndex = i * BLOCK_SIZE; - int endIndex = Math.min(startIndex + BLOCK_SIZE, length); - bodyParts[i] = body.substring(startIndex, endIndex); - } - - return new JsniMethod() { - - public Class<? extends Annotation> annotationType() { - return JsniMethod.class; - } - - public String[] body() { - return bodyParts; - } - - public String file() { - return file; - } - - public int line() { - return line; - } - - public String name() { - return name; - } - - public String[] paramNames() { - return paramNames; - } - - @Override - public String toString() { - StringBuffer sb = new StringBuffer(); - sb.append("@" + JSNIMETHOD_NAME + "(file=\""); - sb.append(Jsni.escapedJavaScriptForStringLiteral(file)); - sb.append("\",line="); - sb.append(line); - sb.append(",name=\"@"); - sb.append(name); - sb.append("\",paramNames={"); - for (String paramName : paramNames) { - sb.append('\"'); - sb.append(paramName); - sb.append('\"'); - sb.append(','); - } - sb.append("},body={"); - for (String bodyPart : bodyParts) { - sb.append('"'); - sb.append(Jsni.escapedJavaScriptForStringLiteral(bodyPart)); - sb.append('"'); - sb.append(','); - } - sb.append("})"); - return sb.toString(); - } - }; - } - - /** - * Generate annotation metadata for all the JSNI methods in a list. - */ - private char[] genJsniMethodsAnnotation(List<JsniMethod> jsniMethods, - boolean pretty) { - StringBuffer sb = new StringBuffer(); - String nl = pretty ? "\n " : ""; - sb.append("@" + JSNIMETHODS_NAME + "({"); - for (JsniMethod jsniMethod : jsniMethods) { - sb.append(jsniMethod.toString()); - sb.append(','); - sb.append(nl); - } - sb.append("})"); - return sb.toString().toCharArray(); - } - - private void rewriteCompilationUnit(TreeLogger logger, char[] source, - List<Replacement> changes, CompilationUnitProvider cup, boolean pretty) - throws UnableToCompleteException { - - // Collect all JSNI methods in the compilation unit. - JClassType[] types = oracle.getTypesInCompilationUnit(cup); - for (JClassType type : types) { - if (!type.getQualifiedSourceName().startsWith("java.")) { - collectJsniMethods(logger, source, type); - } - } - - // Annotate the appropriate types with JsniMethod annotations. - for (JClassType type : types) { - List<JsniMethod> jsniMethods = jsniMethodMap.get(type); - if (jsniMethods != null && jsniMethods.size() > 0) { - char[] annotation = genJsniMethodsAnnotation(jsniMethods, pretty); - int declStart = type.getDeclStart(); - changes.add(new Replacement(declStart, declStart, annotation)); - } - } - } -}
diff --git a/dev/core/src/com/google/gwt/dev/shell/JsniMethods.java b/dev/core/src/com/google/gwt/dev/shell/JsniMethods.java deleted file mode 100644 index 2631851..0000000 --- a/dev/core/src/com/google/gwt/dev/shell/JsniMethods.java +++ /dev/null
@@ -1,67 +0,0 @@ -/* - * Copyright 2008 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.shell; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Encodes all JSNI methods into a compiled hosted mode class file. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface JsniMethods { - - /** - * Encodes a JSNI method into a compiled hosted mode class file. - */ - @Target(value = {}) - public @interface JsniMethod { - /** - * Source file of the method. - */ - String file(); - - /** - * Starting line number of the method. - */ - int line(); - - /** - * The mangled method name (a jsni signature). - */ - String name(); - - /** - * The parameter names. - */ - String[] paramNames(); - - /** - * The script body. The reason this is an array rather than a single string - * is that 64k is the max size of a single string in a class file, and some - * methods (such as TypeSerializer method maps) will exceed this. - */ - String[] body(); - } - - /** - * The set of all methods. - */ - JsniMethod[] value(); -}
diff --git a/dev/core/src/com/google/gwt/dev/shell/ShellGWT.java b/dev/core/src/com/google/gwt/dev/shell/ShellGWT.java deleted file mode 100644 index a38cea1..0000000 --- a/dev/core/src/com/google/gwt/dev/shell/ShellGWT.java +++ /dev/null
@@ -1,40 +0,0 @@ -/* - * Copyright 2007 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.shell; - -import com.google.gwt.dev.About; - -/** - * This class is the hosted-mode peer for {@link com.google.gwt.core.client.GWT}. - */ -public class ShellGWT { - - public static <T> T create(Class<?> classLiteral) { - return JavaScriptHost.<T>rebindAndCreate(classLiteral); - } - - public static String getTypeName(Object o) { - return o != null ? o.getClass().getName() : null; - } - - public static String getVersion() { - return About.GWT_VERSION_NUM; - }; - - public static void log(String message, Throwable e) { - JavaScriptHost.log(message, e); - } -}
diff --git a/dev/core/src/com/google/gwt/dev/shell/ShellModuleSpaceHost.java b/dev/core/src/com/google/gwt/dev/shell/ShellModuleSpaceHost.java index 2175316..39cb165 100644 --- a/dev/core/src/com/google/gwt/dev/shell/ShellModuleSpaceHost.java +++ b/dev/core/src/com/google/gwt/dev/shell/ShellModuleSpaceHost.java
@@ -17,18 +17,13 @@ import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.core.ext.linker.ArtifactSet; import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.dev.cfg.ModuleDef; import com.google.gwt.dev.cfg.Rules; -import com.google.gwt.dev.jdt.ByteCodeCompiler; import com.google.gwt.dev.jdt.RebindOracle; -import com.google.gwt.dev.jdt.SourceOracle; - -import org.apache.commons.collections.map.AbstractReferenceMap; -import org.apache.commons.collections.map.ReferenceIdentityMap; import java.io.File; -import java.util.Map; /** * Provides an environment for a {@link com.google.gwt.dev.shell.ModuleSpace} @@ -36,10 +31,6 @@ */ public class ShellModuleSpaceHost implements ModuleSpaceHost { - @SuppressWarnings("unchecked") - private static Map<ModuleDef, ByteCodeCompiler> byteCodeCompilersByModule = new ReferenceIdentityMap( - AbstractReferenceMap.WEAK, AbstractReferenceMap.HARD, true); - protected final File genDir; protected final TypeOracle typeOracle; @@ -52,8 +43,6 @@ private RebindOracle rebindOracle; - private final boolean saveJsni; - private final File shellDir; private ModuleSpace space; @@ -63,12 +52,11 @@ * @param saveJsni */ public ShellModuleSpaceHost(TreeLogger logger, TypeOracle typeOracle, - ModuleDef module, File genDir, File shellDir, boolean saveJsni) { + ModuleDef module, File genDir, File shellDir) { this.logger = logger; this.typeOracle = typeOracle; this.module = module; this.genDir = genDir; - this.saveJsni = saveJsni; // Combine the user's output dir with the module name to get the // module-specific output dir. @@ -93,16 +81,6 @@ throws UnableToCompleteException { this.space = readySpace; - // Create a host for the hosted mode compiler. - // We add compilation units to it as deferred binding generators write them. - // - SourceOracle srcOracle = new HostedModeSourceOracle(typeOracle, saveJsni - ? genDir : null); - - // Create or find the compiler to be used by the compiling class loader. - // - ByteCodeCompiler compiler = getOrCreateByteCodeCompiler(srcOracle); - // Establish an environment for JavaScript property providers to run. // ModuleSpacePropertyOracle propOracle = new ModuleSpacePropertyOracle( @@ -112,8 +90,8 @@ // It has to wait until now because we need to inject javascript. // Rules rules = module.getRules(); - rebindOracle = new StandardRebindOracle(typeOracle, propOracle, module, - rules, genDir, shellDir, module.getCacheManager(), null); + rebindOracle = new StandardRebindOracle(module.getCompilationState(), + propOracle, module, rules, genDir, shellDir, new ArtifactSet()); // Create a completely isolated class loader which owns all classes // associated with a particular module. This effectively builds a @@ -126,8 +104,8 @@ // accidentally 'escaping' its domain and loading classes from the system // class loader (the one that loaded the shell itself). // - classLoader = new CompilingClassLoader(logger, compiler, typeOracle, - readySpace); + classLoader = new CompilingClassLoader(logger, + module.getCompilationState(), readySpace); } public String rebind(TreeLogger rebindLogger, String sourceTypeName) @@ -136,18 +114,6 @@ return rebindOracle.rebind(rebindLogger, sourceTypeName); } - ByteCodeCompiler getOrCreateByteCodeCompiler(SourceOracle srcOracle) { - ByteCodeCompiler compiler; - synchronized (byteCodeCompilersByModule) { - compiler = byteCodeCompilersByModule.get(module); - if (compiler == null) { - compiler = new ByteCodeCompiler(srcOracle, module.getCacheManager()); - byteCodeCompilersByModule.put(module, compiler); - } - } - return compiler; - } - private void checkForModuleSpace() { if (space == null) { throw new IllegalStateException("Module initialization error");
diff --git a/dev/core/src/com/google/gwt/dev/shell/StandardGeneratorContext.java b/dev/core/src/com/google/gwt/dev/shell/StandardGeneratorContext.java index f8fb4cd..1a3dfcc 100644 --- a/dev/core/src/com/google/gwt/dev/shell/StandardGeneratorContext.java +++ b/dev/core/src/com/google/gwt/dev/shell/StandardGeneratorContext.java
@@ -24,24 +24,21 @@ import com.google.gwt.core.ext.linker.ArtifactSet; import com.google.gwt.core.ext.linker.GeneratedResource; import com.google.gwt.core.ext.linker.impl.StandardGeneratedResource; -import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.NotFoundException; import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.dev.cfg.PublicOracle; -import com.google.gwt.dev.jdt.CacheManager; -import com.google.gwt.dev.jdt.StaticCompilationUnitProvider; -import com.google.gwt.dev.jdt.TypeOracleBuilder; -import com.google.gwt.dev.jdt.URLCompilationUnitProvider; +import com.google.gwt.dev.javac.CompilationState; +import com.google.gwt.dev.javac.CompilationUnit; +import com.google.gwt.dev.javac.impl.Shared; import com.google.gwt.dev.util.Util; import java.io.ByteArrayOutputStream; -import java.io.CharArrayWriter; import java.io.File; import java.io.OutputStream; import java.io.PrintWriter; +import java.io.StringWriter; import java.net.MalformedURLException; -import java.net.URL; import java.util.ArrayList; import java.util.HashSet; import java.util.IdentityHashMap; @@ -49,80 +46,91 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.SortedSet; /** - * An abstract implementation of a generator context in terms of a - * {@link com.google.gwt.dev.jdt.MutableCompilationServiceHost}, a - * {@link com.google.gwt.dev.jdt.PropertyOracle}, and a - * {@link com.google.gwt.core.server.typeinfo.TypeOracle}. The generator - * interacts with the mutable source oracle by increasing the available - * compilation units as they are generated. + * Manages generators and generated units during a single compilation. */ public class StandardGeneratorContext implements GeneratorContext { /** - * This compilation unit provider acts as a normal compilation unit provider - * as well as a buffer into which generators can write their source. A - * controller should ensure that source isn't requested until the generator - * has finished writing it. + * This compilation unit acts as a normal compilation unit as well as a buffer + * into which generators can write their source. A controller should ensure + * that source isn't requested until the generator has finished writing it. */ - private static class GeneratedCompilationUnitProvider extends - StaticCompilationUnitProvider { + private static class GeneratedUnitWithFile extends CompilationUnit { - public CharArrayWriter caw; + private File file; - public PrintWriter pw; + private PrintWriter pw; - public char[] source; + private String source; - public GeneratedCompilationUnitProvider(String packageName, - String simpleTypeName) { - super(packageName, simpleTypeName, null); - caw = new CharArrayWriter(); - pw = new PrintWriter(caw, true); + private StringWriter sw; + + private final String typeName; + + public GeneratedUnitWithFile(String typeName) { + this.typeName = typeName; + sw = new StringWriter(); + pw = new PrintWriter(sw, true); } /** * Finalizes the source and adds this compilation unit to the host. */ public void commit() { - source = caw.toCharArray(); - pw.close(); + source = sw.toString(); pw = null; - caw.close(); - caw = null; + sw = null; } @Override - public char[] getSource() { - if (source == null) { + public String getDisplayLocation() { + if (file == null) { + return "transient source for " + typeName; + } else { + return file.getAbsoluteFile().toURI().toString(); + } + } + + @Override + public String getSource() { + if (source == null && file == null) { throw new IllegalStateException("source not committed"); } + if (source == null) { + source = Util.readFileAsString(file); + } + assert (source != null); return source; } - } - /** - * {@link CompilationUnitProvider} used to represent generated source code - * which is stored on disk. This class is only used if the -gen flag is - * specified. - */ - private static final class GeneratedCUP extends URLCompilationUnitProvider { - private GeneratedCUP(URL url, String name) { - super(url, name); + @Override + public String getTypeName() { + return typeName; } @Override - public long getLastModified() throws UnableToCompleteException { - // Make it seem really old so it won't cause recompiles. - // - return 0L; - } - - @Override - public boolean isTransient() { + public boolean isGenerated() { return true; } + + public boolean isOnDisk() { + return file != null; + } + + public void setFile(File file) { + assert (file.exists() && file.canRead()); + this.file = file; + } + + @Override + protected void dumpSource() { + if (file != null) { + source = null; + } + } } /** @@ -161,9 +169,9 @@ private final ArtifactSet artifactSet; - private final CacheManager cacheManager; + private final Set<GeneratedUnitWithFile> committedGeneratedCups = new HashSet<GeneratedUnitWithFile>(); - private final Set<GeneratedCompilationUnitProvider> committedGeneratedCups = new HashSet<GeneratedCompilationUnitProvider>(); + private final CompilationState compilationState; private Class<? extends Generator> currentGenerator; @@ -179,23 +187,20 @@ private final PublicOracle publicOracle; - private final TypeOracle typeOracle; - - private final Map<PrintWriter, GeneratedCompilationUnitProvider> uncommittedGeneratedCupsByPrintWriter = new IdentityHashMap<PrintWriter, GeneratedCompilationUnitProvider>(); + private final Map<PrintWriter, GeneratedUnitWithFile> uncommittedGeneratedCupsByPrintWriter = new IdentityHashMap<PrintWriter, GeneratedUnitWithFile>(); /** * Normally, the compiler host would be aware of the same types that are * available in the supplied type oracle although it isn't strictly required. */ - public StandardGeneratorContext(TypeOracle typeOracle, + public StandardGeneratorContext(CompilationState compilationState, PropertyOracle propOracle, PublicOracle publicOracle, File genDir, - File outDir, CacheManager cacheManager, ArtifactSet artifactSet) { - this.typeOracle = typeOracle; + File outDir, ArtifactSet artifactSet) { + this.compilationState = compilationState; this.propOracle = propOracle; this.publicOracle = publicOracle; this.genDir = genDir; this.outDir = outDir; - this.cacheManager = cacheManager; this.artifactSet = artifactSet; } @@ -203,7 +208,7 @@ * Commits a pending generated type. */ public final void commit(TreeLogger logger, PrintWriter pw) { - GeneratedCompilationUnitProvider gcup = uncommittedGeneratedCupsByPrintWriter.get(pw); + GeneratedUnitWithFile gcup = uncommittedGeneratedCupsByPrintWriter.get(pw); if (gcup != null) { gcup.commit(); uncommittedGeneratedCupsByPrintWriter.remove(pw); @@ -221,9 +226,7 @@ public void commitArtifact(TreeLogger logger, Artifact<?> artifact) throws UnableToCompleteException { // The artifactSet will be null in hosted mode, since we never run Linkers - if (artifactSet != null) { - artifactSet.replace(artifact); - } + artifactSet.replace(artifact); } public GeneratedResource commitResource(TreeLogger logger, OutputStream os) @@ -234,7 +237,6 @@ if (pendingResource != null) { // Actually write the bytes to disk. pendingResource.commit(logger); - cacheManager.addGeneratedResource(pendingResource.getPartialPath()); // Add the GeneratedResource to the ArtifactSet GeneratedResource toReturn; @@ -293,26 +295,22 @@ "Generated source files...", null); } - assert (cacheManager.getTypeOracle() == typeOracle); - TypeOracleBuilder builder = new TypeOracleBuilder(cacheManager); - for (Iterator<GeneratedCompilationUnitProvider> iter = committedGeneratedCups.iterator(); iter.hasNext();) { - GeneratedCompilationUnitProvider gcup = iter.next(); - String typeName = gcup.getTypeName(); - String genTypeName = gcup.getPackageName() + "." + typeName; - genTypeNames.add(genTypeName); - CompilationUnitProvider cup = writeSource(logger, gcup, typeName); - builder.addCompilationUnit(cup); - cacheManager.addGeneratedCup(cup); + for (GeneratedUnitWithFile gcup : committedGeneratedCups) { + String qualifiedTypeName = gcup.getTypeName(); + genTypeNames.add(qualifiedTypeName); + maybeWriteSource(gcup, qualifiedTypeName); + compilationState.addGeneratedCompilationUnit(gcup); if (subBranch != null) { - subBranch.log(TreeLogger.DEBUG, cup.getLocation(), null); + subBranch.log(TreeLogger.DEBUG, gcup.getDisplayLocation(), null); } } - builder.build(branch); + compilationState.compile(logger); } // Return the generated types. + TypeOracle typeOracle = getTypeOracle(); JClassType[] genTypes = new JClassType[genTypeNames.size()]; int next = 0; for (Iterator<String> iter = genTypeNames.iterator(); iter.hasNext();) { @@ -333,10 +331,8 @@ String msg = "For the following type(s), generated source was never committed (did you forget to call commit()?)"; logger = logger.branch(TreeLogger.WARN, msg, null); - for (Iterator<GeneratedCompilationUnitProvider> iter = uncommittedGeneratedCupsByPrintWriter.values().iterator(); iter.hasNext();) { - StaticCompilationUnitProvider cup = iter.next(); - String typeName = cup.getPackageName() + "." + cup.getTypeName(); - logger.log(TreeLogger.WARN, typeName, null); + for (GeneratedUnitWithFile unit : uncommittedGeneratedCupsByPrintWriter.values()) { + logger.log(TreeLogger.WARN, unit.getTypeName(), null); } } @@ -355,7 +351,7 @@ } public final TypeOracle getTypeOracle() { - return typeOracle; + return compilationState.getTypeOracle(); } public void setCurrentGenerator(Class<? extends Generator> currentGenerator) { @@ -367,7 +363,8 @@ String typeName = packageName + "." + simpleTypeName; // Is type already known to the host? - JClassType existingType = typeOracle.findType(packageName, simpleTypeName); + JClassType existingType = getTypeOracle().findType(packageName, + simpleTypeName); if (existingType != null) { logger.log(TreeLogger.DEBUG, "Type '" + typeName + "' already exists and will not be re-created ", null); @@ -385,8 +382,13 @@ // The type isn't there, so we can let the caller create it. Remember that // it is pending so another attempt to create the same type will fail. - GeneratedCompilationUnitProvider gcup = new GeneratedCompilationUnitProvider( - packageName, simpleTypeName); + String qualifiedSourceName; + if (packageName.length() == 0) { + qualifiedSourceName = simpleTypeName; + } else { + qualifiedSourceName = packageName + '.' + simpleTypeName; + } + GeneratedUnitWithFile gcup = new GeneratedUnitWithFile(qualifiedSourceName); uncommittedGeneratedCupsByPrintWriter.put(gcup.pw, gcup); generatedTypeNames.add(typeName); @@ -433,8 +435,11 @@ } // See if the file is already committed. - if (cacheManager.hasGeneratedResource(partialPath)) { - return null; + SortedSet<GeneratedResource> resources = artifactSet.find(GeneratedResource.class); + for (GeneratedResource resource : resources) { + if (partialPath.equals(resource.getPartialPath())) { + return null; + } } // See if the file is pending. @@ -483,43 +488,25 @@ * Writes the source of the specified compilation unit to disk if a gen * directory is specified. * - * @param cup the compilation unit whose contents might need to be written - * @param simpleTypeName the fully-qualified type name - * @return a wrapper for the existing cup with a proper location + * @param unit the compilation unit whose contents might need to be written + * @param qualifiedTypeName the fully-qualified type name */ - private CompilationUnitProvider writeSource(TreeLogger logger, - CompilationUnitProvider cup, String simpleTypeName) - throws UnableToCompleteException { + private void maybeWriteSource(GeneratedUnitWithFile unit, + String qualifiedTypeName) { - if (genDir == null) { + if (unit.isOnDisk() || genDir == null) { // No place to write it. - return cup; - } - - if (Util.isCompilationUnitOnDisk(cup.getLocation())) { - // Already on disk. - return cup; + return; } // Let's do write it. - String typeName = cup.getPackageName() + "." + simpleTypeName; - String relativePath = typeName.replace('.', '/') + ".java"; - File srcFile = new File(genDir, relativePath); - Util.writeCharsAsFile(logger, srcFile, cup.getSource()); - - // Update the location of the cup - Throwable caught = null; - try { - URL fileURL = srcFile.toURI().toURL(); - URLCompilationUnitProvider fileBaseCup = new GeneratedCUP(fileURL, - cup.getPackageName()); - return fileBaseCup; - } catch (MalformedURLException e) { - caught = e; + String packageName = Shared.getPackageName(qualifiedTypeName); + String shortName = Shared.getShortName(qualifiedTypeName); + File dir = new File(genDir, packageName.replace('.', File.separatorChar)); + dir.mkdirs(); + File srcFile = new File(dir, shortName + ".java"); + if (Util.writeStringAsFile(srcFile, unit.getSource())) { + unit.setFile(srcFile); } - logger.log(TreeLogger.ERROR, - "Internal error: cannot build URL from synthesized file name '" - + srcFile.getAbsolutePath() + "'", caught); - throw new UnableToCompleteException(); } }
diff --git a/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java b/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java index 554a351..c338a91 100644 --- a/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java +++ b/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java
@@ -1,5 +1,5 @@ /* - * Copyright 2007 Google Inc. + * Copyright 2008 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 @@ -20,11 +20,10 @@ import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.linker.ArtifactSet; import com.google.gwt.core.ext.typeinfo.JClassType; -import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.dev.cfg.PublicOracle; import com.google.gwt.dev.cfg.Rule; import com.google.gwt.dev.cfg.Rules; -import com.google.gwt.dev.jdt.CacheManager; +import com.google.gwt.dev.javac.CompilationState; import com.google.gwt.dev.jdt.RebindOracle; import com.google.gwt.dev.util.Util; @@ -51,10 +50,9 @@ private final List<String> usedTypeNames = new ArrayList<String>(); - public Rebinder(TypeOracle typeOracle, PropertyOracle propOracle, - PublicOracle publicOracle) { - genCtx = new StandardGeneratorContext(typeOracle, propOracle, - publicOracle, genDir, outDir, cacheManager, artifactSet); + public Rebinder() { + genCtx = new StandardGeneratorContext(compilationState, propOracle, + publicOracle, genDir, outDir, artifactSet); } public String rebind(TreeLogger logger, String typeName) @@ -135,7 +133,7 @@ private final ArtifactSet artifactSet; - private final CacheManager cacheManager; + private final CompilationState compilationState; private final File genDir; @@ -147,22 +145,15 @@ private final Rules rules; - private final TypeOracle typeOracle; - - public StandardRebindOracle(TypeOracle typeOracle, PropertyOracle propOracle, - PublicOracle publicOracle, Rules rules, File genDir, File moduleOutDir, - CacheManager cacheManager, ArtifactSet artifactSet) { - this.typeOracle = typeOracle; + public StandardRebindOracle(CompilationState compilationState, + PropertyOracle propOracle, PublicOracle publicOracle, Rules rules, + File genDir, File moduleOutDir, ArtifactSet artifactSet) { + this.compilationState = compilationState; this.propOracle = propOracle; this.publicOracle = publicOracle; this.rules = rules; this.genDir = genDir; this.outDir = moduleOutDir; - if (cacheManager != null) { - this.cacheManager = cacheManager; - } else { - this.cacheManager = new CacheManager(typeOracle); - } this.artifactSet = artifactSet; } @@ -171,7 +162,7 @@ logger = Messages.TRACE_TOPLEVEL_REBIND.branch(logger, typeName, null); - Rebinder rebinder = new Rebinder(typeOracle, propOracle, publicOracle); + Rebinder rebinder = new Rebinder(); String result = rebinder.rebind(logger, typeName); Messages.TRACE_TOPLEVEL_REBIND_RESULT.log(logger, result, null);
diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteJsniMethods.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteJsniMethods.java index 54a536f..a00ce8d 100644 --- a/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteJsniMethods.java +++ b/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteJsniMethods.java
@@ -367,7 +367,6 @@ + descriptor; String argsDescriptor = descriptor.substring(argsIndexBegin, argsIndexEnd + 1); - String sourceName = classDesc.replace('/', '.').replace('$', '.'); - return "@" + sourceName + "::" + name + argsDescriptor; + return "@" + classDesc.replace('/', '.') + "::" + name + argsDescriptor; } }
diff --git a/dev/core/src/com/google/gwt/dev/shell/tomcat/EmbeddedTomcatServer.java b/dev/core/src/com/google/gwt/dev/shell/tomcat/EmbeddedTomcatServer.java index ae78db3..723a431 100644 --- a/dev/core/src/com/google/gwt/dev/shell/tomcat/EmbeddedTomcatServer.java +++ b/dev/core/src/com/google/gwt/dev/shell/tomcat/EmbeddedTomcatServer.java
@@ -16,8 +16,11 @@ package com.google.gwt.dev.shell.tomcat; import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.dev.util.FileOracle; -import com.google.gwt.dev.util.FileOracleFactory; +import com.google.gwt.dev.resource.Resource; +import com.google.gwt.dev.resource.impl.ClassPathEntry; +import com.google.gwt.dev.resource.impl.PathPrefix; +import com.google.gwt.dev.resource.impl.PathPrefixSet; +import com.google.gwt.dev.resource.impl.ResourceOracleImpl; import com.google.gwt.util.tools.Utility; import org.apache.catalina.Connector; @@ -39,7 +42,11 @@ import java.lang.reflect.Field; import java.net.InetAddress; import java.net.ServerSocket; +import java.net.URISyntaxException; import java.net.URL; +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; /** * Wraps an instance of the Tomcat web server used in hosted mode. @@ -249,14 +256,14 @@ /* * Assumes that the leaf is a file (not a directory). */ - private void copyFileNoOverwrite(TreeLogger logger, FileOracle fileOracle, - String srcResName, File catBase) { + private void copyFileNoOverwrite(TreeLogger logger, String srcResName, + Resource resource, File catBase) { File dest = new File(catBase, srcResName); InputStream is = null; FileOutputStream os = null; try { - URL srcRes = fileOracle.find(srcResName); + URL srcRes = resource.getURL(); if (srcRes == null) { logger.log(TreeLogger.TRACE, "Cannot find: " + srcResName, null); return; @@ -321,28 +328,61 @@ "Property 'catalina.base' not specified; checking for a standard catalina base image instead", null); - // Recursively copies out files and directories under - // com.google.gwt.dev.etc.tomcat. - // - FileOracleFactory fof = new FileOracleFactory(); - final String tomcatEtcDir = "com/google/gwt/dev/etc/tomcat/"; - fof.addRootPackage(tomcatEtcDir, null); - FileOracle fo = fof.create(logger); - if (fo.isEmpty()) { - logger.log(TreeLogger.WARN, "Could not find " + tomcatEtcDir, null); - return null; + // Recursively copies out files and directories + String tomcatEtcDir = "com/google/gwt/dev/etc/tomcat/"; + Map<String, Resource> resourceMap = null; + Throwable caught = null; + try { + resourceMap = getResourcesFor(logger, tomcatEtcDir); + } catch (URISyntaxException e) { + caught = e; + } catch (IOException e) { + caught = e; } File catBase = new File(workDir, "tomcat"); - String[] allChildren = fo.getAllFiles(); - for (int i = 0; i < allChildren.length; i++) { - String src = allChildren[i]; - copyFileNoOverwrite(logger, fo, src, catBase); + if (resourceMap == null || resourceMap.isEmpty()) { + logger.log(TreeLogger.WARN, "Could not find " + tomcatEtcDir, caught); + } else { + for (Entry<String, Resource> entry : resourceMap.entrySet()) { + copyFileNoOverwrite(logger, entry.getKey(), entry.getValue(), catBase); + } } return catBase.getAbsolutePath(); } + /** + * Hacky, but fast. + */ + private Map<String, Resource> getResourcesFor(TreeLogger logger, + String tomcatEtcDir) throws URISyntaxException, IOException { + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + URL url = contextClassLoader.getResource(tomcatEtcDir); + if (url == null) { + return null; + } + String prefix = ""; + String urlString = url.toString(); + if (urlString.startsWith("jar:")) { + assert urlString.contains(".jar!/" + tomcatEtcDir); + urlString = urlString.substring(4, urlString.indexOf('!')); + url = new URL(urlString); + prefix = tomcatEtcDir; + } + ClassPathEntry entry = ResourceOracleImpl.createEntryForUrl(logger, url); + assert (entry != null); + ResourceOracleImpl resourceOracle = new ResourceOracleImpl( + Collections.singletonList(entry)); + PathPrefixSet pathPrefixSet = new PathPrefixSet(); + PathPrefix pathPrefix = new PathPrefix(prefix, null, true); + pathPrefixSet.add(pathPrefix); + resourceOracle.setPathPrefixes(pathPrefixSet); + resourceOracle.refresh(logger); + Map<String, Resource> resourceMap = resourceOracle.getResourceMap(); + return resourceMap; + } + private void publishAttributeToWebApp(TreeLogger logger, StandardContext webapp, String attrName, Object attrValue) { logger.log(TreeLogger.TRACE, "Adding attribute '" + attrName
diff --git a/dev/core/src/com/google/gwt/dev/util/FileOracle.java b/dev/core/src/com/google/gwt/dev/util/FileOracle.java deleted file mode 100644 index e44a207..0000000 --- a/dev/core/src/com/google/gwt/dev/util/FileOracle.java +++ /dev/null
@@ -1,57 +0,0 @@ -/* - * Copyright 2006 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.util; - -import java.net.URL; - -/** - * An abstraction for finding and retrieving a set of URLs by logical name. - * Intuitively, it works like a jar in that each URL is uniquely located - * somewhere in an abstract namespace. The abstract names must be constructed - * from a series of zero or more valid Java identifiers followed the '/' - * character and finally ending in a valid filename, for example, - * "com/google/gwt/blah.txt". Each contained abstract path corresponds to a - * physical URL. - */ -public abstract class FileOracle { - - /** - * Finds a URL by abstract path. - * - * @param abstractPath the abstract path of the URL to find. - * @return the physical URL of the contained URL, or <code>null</code> the - * abstract path does not refer to a contained URL. - */ - public abstract URL find(String abstractPath); - - /** - * Gets the abstract path for every URL indexed by this FileOracle. Elements - * of the result set can be passed into {@link #find(String)} to retrieve the - * physical URL. - * - * @return the abstract path of every URL indexed by this FileOracle - */ - public abstract String[] getAllFiles(); - - /** - * Tests if this FileOracle has URLs. - * - * @return <tt>true</tt> if this list has no elements; <tt>false</tt> - * otherwise. - */ - public abstract boolean isEmpty(); - -}
diff --git a/dev/core/src/com/google/gwt/dev/util/FileOracleFactory.java b/dev/core/src/com/google/gwt/dev/util/FileOracleFactory.java deleted file mode 100644 index 70d80e1..0000000 --- a/dev/core/src/com/google/gwt/dev/util/FileOracleFactory.java +++ /dev/null
@@ -1,488 +0,0 @@ -/* - * Copyright 2006 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.util; - -import com.google.gwt.core.ext.TreeLogger; - -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; - -/** - * Creates a FileOracle based on a set of logical packages combined with either - * a URLClassLoader. For each specified package, the ClassLoader is searched for - * instances of that package as a directory. The results of this operation are - * merged together into a single list of URLs whose order is determined by the - * order of URLs in the ClassLoader. The relative order of different logical - * packages originating from the same URL in the ClassLoader is undefined. - * - * Once the sorted list of URLs is resolved, each URL is recursively searched to - * index all of its files (optionally, that pass the given FileOracleFilter). - * The results of this indexing are used to create the output FileOracle. Once - * the FileOracle is created, its index is fixed and no longer depends on the - * underlying URLClassLoader or file system. However, URLs returned from the - * FileOracle may become invalid if the contents of the file system change. - * - * Presently, only URLs beginning with <code>file:</code> and - * <code>jar:file:</code> can be inspected to index children. Any other types - * of URLs will generate a warning. The set of children indexed by - * <code>jar:file:</code> type URLs is fixed at creation time, but the set of - * children from <code>file:</code> type URLs will dynamically query the - * underlying file system. - */ -public class FileOracleFactory { - - /** - * Used to decide whether or not a resource name should be included in an - * enumeration. - */ - public interface FileFilter { - boolean accept(String name); - } - - /** - * Implementation of a FileOracle as an ordered (based on class path) list of - * abstract names (relative to some root), each mapped to a concrete URL. - */ - private static final class FileOracleImpl extends FileOracle { - - private final String[] logicalNames; - - private final Map logicalToPhysical; - - /** - * Creates a new FileOracle. - * - * @param logicalNames An ordered list of abstract path name strings. - * @param logicalToPhysical A map of every item in logicalNames onto a URL. - */ - public FileOracleImpl(List logicalNames, Map logicalToPhysical) { - this.logicalNames = (String[]) logicalNames.toArray(new String[logicalNames.size()]); - this.logicalToPhysical = new HashMap(logicalToPhysical); - } - - /* - * (non-Javadoc) - * - * @see com.google.gwt.dev.util.FileOracle#find(java.lang.String) - */ - public URL find(String partialPath) { - return (URL) logicalToPhysical.get(partialPath); - } - - /* - * (non-Javadoc) - * - * @see com.google.gwt.dev.util.FileOracle#getAllFiles() - */ - public String[] getAllFiles() { - return logicalNames; - } - - /* - * (non-Javadoc) - * - * @see com.google.gwt.dev.util.FileOracle#isEmpty() - */ - public boolean isEmpty() { - return logicalNames.length == 0; - } - } - - /** - * Given a set of logical packages, finds every occurrence of each of those - * packages within cl, and then sorts them relative to each other based on - * classPathUrlList. - * - * @param logger Logs the process. - * @param cl Provides the underlying class path. - * @param packageSet The input set of logical packages to search for and sort. - * @param classPathUrlList The order in which to sort the results. - * @param sortedUrls An output list to which urls are appended. - * @param sortedPackages An output list to which logical packages are appended - * exactly corresponding to appends made to sortedUrls. - * @param recordPackages If false, only empty strings are appended to - * sortedPackages. - */ - private static void addPackagesInSortedOrder(TreeLogger logger, - URLClassLoader cl, Map packageMap, List classPathUrlList, - List sortedUrls, List sortedPackages, List sortedFilters, - boolean recordPackages) { - - // Exhaustively find every package on the classpath in an unsorted fashion - // - List unsortedUrls = new ArrayList(); - List unsortedPackages = new ArrayList(); - List unsortedFilters = new ArrayList(); - for (Iterator itPkg = packageMap.keySet().iterator(); itPkg.hasNext();) { - String curPkg = (String) itPkg.next(); - FileFilter curFilter = (FileFilter) packageMap.get(curPkg); - try { - Enumeration found = cl.findResources(curPkg); - if (!recordPackages) { - curPkg = ""; - } - while (found.hasMoreElements()) { - URL match = (URL) found.nextElement(); - unsortedUrls.add(match); - unsortedPackages.add(curPkg); - unsortedFilters.add(curFilter); - } - } catch (IOException e) { - logger.log(TreeLogger.WARN, "Unexpected error searching classpath for " - + curPkg, e); - } - } - - /* - * Now sort the collected list by the proper class path order. This is an - * O(N*M) operation, but it should be okay for what we're doing - */ - - // pre-convert the List of URL to String[] to speed up the inner loop below - int c = unsortedUrls.size(); - String[] unsortedUrlStrings = new String[c]; - for (int i = 0; i < c; ++i) { - unsortedUrlStrings[i] = unsortedUrls.get(i).toString(); - // strip the jar prefix for text matching purposes - if (unsortedUrlStrings[i].startsWith("jar:")) { - unsortedUrlStrings[i] = unsortedUrlStrings[i].substring(4); - } - } - - // now sort the URLs based on classPathUrlList - for (Iterator itCp = classPathUrlList.iterator(); itCp.hasNext();) { - URL curCpUrl = (URL) itCp.next(); - String curUrlString = curCpUrl.toExternalForm(); - // find all URLs that match this particular entry - for (int i = 0; i < c; ++i) { - if (unsortedUrlStrings[i].startsWith(curUrlString)) { - sortedUrls.add(unsortedUrls.get(i)); - sortedPackages.add(unsortedPackages.get(i)); - sortedFilters.add(unsortedFilters.get(i)); - } - } - } - } - - /** - * Index all the children of a particular folder (recursively). - * - * @param logger Logs the process. - * @param filter If non-null, filters out which files get indexed. - * @param stripBaseLen The number of characters to strip from the beginning of - * every child's file path when computing the logical name. - * @param curDir The directory to index. - * @param logicalNames An output List of Children found under this URL. - * @param logicalToPhysical An output Map of Children found under this URL - * mapped to their concrete URLs. - */ - private static void indexFolder(TreeLogger logger, FileFilter filter, - int stripBaseLen, File curDir, List logicalNames, Map logicalToPhysical) { - File[] files = curDir.listFiles(); - for (int i = 0; i < files.length; i++) { - File f = files[i]; - if (f.exists()) { - if (f.isDirectory()) { - indexFolder(logger, filter, stripBaseLen, f, logicalNames, - logicalToPhysical); - } else if (f.isFile()) { - try { - String logicalName = f.getAbsolutePath().substring(stripBaseLen); - logicalName = logicalName.replace(File.separatorChar, '/'); - if (logicalToPhysical.containsKey(logicalName)) { - // this logical name is shadowed - logger.log(TreeLogger.DEBUG, "Ignoring already-resolved " - + logicalName, null); - continue; - } - if (filter != null && !filter.accept(logicalName)) { - // filtered out - logger.log(TreeLogger.SPAM, "Filtered out " + logicalName, null); - continue; - } - URL physicalUrl = f.toURL(); - logicalToPhysical.put(logicalName, physicalUrl); - logicalNames.add(logicalName); - logger.log(TreeLogger.TRACE, "Found " + logicalName, null); - } catch (IOException e) { - logger.log(TreeLogger.WARN, "Unexpected error resolving " + f, e); - } - } - } - } - } - - /** - * Index all the children in a particular folder of a jar. - * - * @param logger Logs the process. - * @param filter If non-null, filters out which files get indexed. - * @param jarUrl The URL of the containing jar file. - * @param jarFile The jarFile to index. - * @param basePath The sub tree within the jarFile to index. - * @param pkgBase If non-empty, causes the logical names of children to be - * shorter (rooting them higher in the tree). - * @param logicalNames An output List of Children found under this URL. - * @param logicalToPhysical An output Map of Children found under this URL - * mapped to their concrete URLs. - */ - private static void indexJar(TreeLogger logger, FileFilter filter, - String jarUrl, JarFile jarFile, String basePath, String pkgBase, - List logicalNames, Map logicalToPhysical) { - int prefixCharsToStrip = basePath.length() - pkgBase.length(); - for (Enumeration enumJar = jarFile.entries(); enumJar.hasMoreElements();) { - JarEntry jarEntry = (JarEntry) enumJar.nextElement(); - String jarEntryName = jarEntry.getName(); - if (jarEntryName.startsWith(basePath) && !jarEntry.isDirectory()) { - String logicalName = jarEntryName.substring(prefixCharsToStrip); - String physicalUrlString = jarUrl + "!/" + jarEntryName; - if (logicalToPhysical.containsKey(logicalName)) { - // this logical name is shadowed - logger.log(TreeLogger.DEBUG, "Ignoring already-resolved " - + logicalName, null); - continue; - } - if (filter != null && !filter.accept(logicalName)) { - // filtered out - logger.log(TreeLogger.SPAM, "Filtered out " + logicalName, null); - continue; - } - try { - URL physicalUrl = new URL(physicalUrlString); - logicalToPhysical.put(logicalName, physicalUrl); - logicalNames.add(logicalName); - logger.log(TreeLogger.TRACE, "Found " + logicalName, null); - } catch (MalformedURLException e) { - logger.log(TreeLogger.WARN, "Unexpected error resolving " - + physicalUrlString, e); - } - } - } - } - - /** - * Finds all children of the specified URL and indexes them. - * - * @param logger Logs the process. - * @param filter If non-null, filters out which files get indexed. - * @param url The URL to index, must be <code>file:</code> or - * <code>jar:file:</code> - * @param pkgBase A prefix to exclude when indexing children. - * @param logicalNames An output List of Children found under this URL. - * @param logicalToPhysical An output Map of Children found under this URL - * mapped to their concrete URLs. - * @throws URISyntaxException if an unexpected error occurs. - * @throws IOException if an unexpected error occurs. - */ - private static void indexURL(TreeLogger logger, FileFilter filter, URL url, - String pkgBase, List logicalNames, Map logicalToPhysical) - throws URISyntaxException, IOException { - - String urlString = url.toString(); - if (url.getProtocol().equals("file")) { - URI uri = new URI(urlString); - File f = new File(uri); - if (f.isDirectory()) { - int prefixCharsToStrip = f.getAbsolutePath().length() + 1 - - pkgBase.length(); - indexFolder(logger, filter, prefixCharsToStrip, f, logicalNames, - logicalToPhysical); - } else { - // We can't handle files here, only directories. If this is a jar - // reference, the url must come in as a "jar:file:<stuff>!/[stuff/]". - // Fall through. - logger.log(TreeLogger.WARN, "Unexpected error, " + f - + " is neither a file nor a jar", null); - } - } else if (url.getProtocol().equals("jar")) { - String path = url.getPath(); - int pos = path.indexOf('!'); - if (pos >= 0) { - String jarPath = path.substring(0, pos); - String dirPath = path.substring(pos + 2); - URL jarURL = new URL(jarPath); - if (jarURL.getProtocol().equals("file")) { - URI jarURI = new URI(jarURL.toString()); - File f = new File(jarURI); - JarFile jarFile = new JarFile(f); - // From each child, strip off the leading classpath portion when - // determining the logical name (sans the pkgBase name we want!) - // - indexJar(logger, filter, "jar" + ":" + jarPath, jarFile, dirPath, pkgBase, - logicalNames, logicalToPhysical); - } else { - logger.log(TreeLogger.WARN, "Unexpected error, jar at " + jarURL - + " must be a file: type URL", null); - } - } else { - throw new URISyntaxException(path, "Cannot locate '!' separator"); - } - } else { - logger.log(TreeLogger.WARN, "Unknown URL type for " + urlString, null); - } - } - - /** - * The underlying classloader. - */ - private final URLClassLoader classLoader; - - /** - * A map of packages indexed from the root of the class path onto their - * corresponding FileFilters. - */ - private final Map packages = new HashMap(); - - /** - * A map of packages that become their own roots (that is their children are - * indexed relative to them) onto their corresponding FileFilters. - */ - private final Map rootPackages = new HashMap(); - - /** - * Creates a FileOracleFactory with the default URLClassLoader. - */ - public FileOracleFactory() { - this((URLClassLoader) FileOracleFactory.class.getClassLoader()); - } - - /** - * Creates a FileOracleFactory. - * - * @param classLoader The underlying class path to use. - */ - public FileOracleFactory(URLClassLoader classLoader) { - this.classLoader = classLoader; - } - - /** - * Adds a logical package to the product FileOracle. All instances of this - * package that can be found in the underlying URLClassLoader will have their - * their children indexed, relative to the class path entry on which they are - * found. - * - * @param packageAsPath For example, "com/google/gwt/core/client". - */ - public void addPackage(String packageAsPath, FileFilter filter) { - packageAsPath = ensureTrailingBackslash(packageAsPath); - packages.put(packageAsPath, filter); - } - - /** - * Adds a logical root package to the product FileOracle. All instances of - * this package that can be found in the underlying URLClassLoader will have - * their their children indexed, relative to their location within - * packageAsPath. All root packages trump all non-root packages when - * determining the final precedence order. - * - * @param packageAsPath For example, "com/google/gwt/core/client". - */ - public void addRootPackage(String packageAsPath, FileFilter filter) { - packageAsPath = ensureTrailingBackslash(packageAsPath); - rootPackages.put(packageAsPath, filter); - } - - /** - * Creates the product FileOracle based on the logical packages previously - * added. - * - * @param logger Logs the process. - * @return a new FileOracle. - */ - public FileOracle create(TreeLogger logger) { - - // get the full expanded URL class path for sorting purposes - // - List classPathUrls = new ArrayList(); - for (ClassLoader curCL = classLoader; curCL != null; curCL = curCL.getParent()) { - if (curCL instanceof URLClassLoader) { - URLClassLoader curURLCL = (URLClassLoader) curCL; - URL[] curURLs = curURLCL.getURLs(); - classPathUrls.addAll(Arrays.asList(curURLs)); - } - } - - /* - * Collect a sorted list of URLs corresponding to all of the logical - * packages mapped onto the - */ - - // The list of - List urls = new ArrayList(); - List pkgNames = new ArrayList(); - List filters = new ArrayList(); - - // don't record package names for root packages, they are rebased - addPackagesInSortedOrder(logger, classLoader, rootPackages, classPathUrls, - urls, pkgNames, filters, false); - // record package names for non-root packages - addPackagesInSortedOrder(logger, classLoader, packages, classPathUrls, - urls, pkgNames, filters, true); - - // We have a complete sorted list of mapped URLs with package prefixes - - // Setup data collectors - List logicalNames = new ArrayList(); - Map logicalToPhysical = new HashMap(); - - for (int i = 0, c = urls.size(); i < c; ++i) { - try { - URL url = (URL) urls.get(i); - String pkgName = (String) pkgNames.get(i); - FileFilter filter = (FileFilter) filters.get(i); - TreeLogger branch = logger.branch(TreeLogger.TRACE, url.toString(), - null); - indexURL(branch, filter, url, pkgName, logicalNames, logicalToPhysical); - } catch (URISyntaxException e) { - logger.log(TreeLogger.WARN, - "Unexpected error searching " + urls.get(i), e); - } catch (IOException e) { - logger.log(TreeLogger.WARN, - "Unexpected error searching " + urls.get(i), e); - } - } - - return new FileOracleImpl(logicalNames, logicalToPhysical); - } - - /** - * Helper method to regularize packages. - * - * @param packageAsPath For exmaple, "com/google/gwt/core/client" - * @return For example, "com/google/gwt/core/client/" - */ - private String ensureTrailingBackslash(String packageAsPath) { - if (packageAsPath.endsWith("/")) { - return packageAsPath; - } else { - return packageAsPath + "/"; - } - } -}
diff --git a/dev/core/src/com/google/gwt/dev/util/Jsni.java b/dev/core/src/com/google/gwt/dev/util/Jsni.java index b316b0b..801a192 100644 --- a/dev/core/src/com/google/gwt/dev/util/Jsni.java +++ b/dev/core/src/com/google/gwt/dev/util/Jsni.java
@@ -16,50 +16,21 @@ package com.google.gwt.dev.util; import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.core.ext.typeinfo.JMethod; -import com.google.gwt.core.ext.typeinfo.JParameter; -import com.google.gwt.core.ext.typeinfo.JType; -import com.google.gwt.dev.js.JsParser; -import com.google.gwt.dev.js.JsParserException; +import com.google.gwt.dev.javac.JsniMethod; import com.google.gwt.dev.js.JsSourceGenerationVisitor; -import com.google.gwt.dev.js.JsParserException.SourceDetail; -import com.google.gwt.dev.js.ast.JsBlock; import com.google.gwt.dev.js.ast.JsContext; -import com.google.gwt.dev.js.ast.JsExprStmt; import com.google.gwt.dev.js.ast.JsExpression; import com.google.gwt.dev.js.ast.JsFunction; import com.google.gwt.dev.js.ast.JsNameRef; import com.google.gwt.dev.js.ast.JsNode; -import com.google.gwt.dev.js.ast.JsProgram; -import com.google.gwt.dev.js.ast.JsStatement; import com.google.gwt.dev.shell.JavaScriptHost; -import java.io.IOException; -import java.io.StringReader; -import java.util.List; - /** * Helper methods working with JSNI. */ public class Jsni { /** - * Represents a logical interval of text. - */ - public static class Interval { - - public final int end; - - public final int start; - - public Interval(int start, int end) { - this.start = start; - this.end = end; - } - } - - /** * Generate source code, fixing up any JSNI references for hosted mode. * * <p/><table> @@ -127,205 +98,36 @@ public static final String JSNI_BLOCK_START = "/*-{"; /** - * Generates the code to wrap a set of parameters as an object array. In Java - * 1.5 we can take advantage of autoboxing to not have to wrap primitives. + * Gets the body of a JSNI method, with Java refs escaped for hosted mode + * injection. */ - public static String buildArgList(JMethod method) { - StringBuilder sb = new StringBuilder(); - sb.append("new Object[]{"); - - JParameter[] params = method.getParameters(); - for (int i = 0; i < params.length; ++i) { - sb.append(params[i].getName()); - sb.append(", "); - } - - sb.append("}"); - String args = sb.toString(); - return args; - } - - /** - * Generates the code to pass the exact types associated with each argument of - * this method. - */ - public static String buildTypeList(JMethod method) { - StringBuilder sb = new StringBuilder(); - sb.append("new Class[]{"); - - JParameter[] params = method.getParameters(); - for (int i = 0; i < params.length; ++i) { - JType type = params[i].getType(); - String typeName = type.getErasedType().getQualifiedSourceName(); - sb.append(typeName); - sb.append(".class, "); - } - - sb.append("}"); - String classes = sb.toString(); - return classes; - } - - public static int countNewlines(char[] buf, int start, int end) { - int total = 0; - while (start < end) { - switch (buf[start]) { - case '\r': - ++total; - // if the next character is a line feed, eat it too - if (start + 1 < end && buf[start + 1] == '\n') { - ++start; - } - break; - case '\n': - ++total; - break; - } - ++start; - } - return total; - } - - public static int countNewlines(String src, int start, int end) { - return countNewlines(src.toCharArray(), start, end); - } - - /** - * Replaces double-quotes, backslashes, and newlines in native JS code with - * their appropriate escaped form (so they can be encoded in a java string). - */ - public static String escapedJavaScriptForStringLiteral(String js) { - StringBuilder sb = new StringBuilder(js); - for (int i = 0; i < sb.length(); ++i) { - char c = sb.charAt(i); - switch (c) { - case '\"': - case '\\': - sb.insert(i, '\\'); - ++i; - break; - case '\r': - sb.setCharAt(i, 'r'); - sb.insert(i, '\\'); - ++i; - break; - case '\n': - sb.setCharAt(i, 'n'); - sb.insert(i, '\\'); - ++i; - break; - } - } - return sb.toString(); - } - - public static Interval findJsniSource(JMethod method) - throws UnableToCompleteException { - assert (method.isNative()); - int bodyStart = method.getBodyStart(); - int bodyEnd = method.getBodyEnd(); - int bodyLen = bodyEnd - bodyStart + 1; - char[] source = method.getEnclosingType().getCompilationUnit().getSource(); - String js = String.valueOf(source, bodyStart, bodyLen); - - int jsniStart = js.indexOf(JSNI_BLOCK_START); - if (jsniStart == -1) { + public static String getJavaScriptForHostedMode(TreeLogger logger, + JsniMethod jsniMethod) { + /* + * Surround the original JS body statements with a try/catch so that we can + * map JavaScript exceptions back into Java. Note that the method body + * itself will print curly braces, so we don't need them around the + * try/catch. + */ + String jsTry = "try "; + String jsCatch = " catch (e) {\n __static[\"@" + Jsni.JAVASCRIPTHOST_NAME + + "::exceptionCaught(Ljava/lang/Object;)\"](e);\n" + "}\n"; + JsFunction func = jsniMethod.function(logger); + if (func == null) { return null; } - - int jsniEnd = js.indexOf(JSNI_BLOCK_END, jsniStart); - if (jsniEnd == -1) { - // Suspicious, but maybe this is just a weird comment, so let it slide. - // - return null; - } - - int srcStart = bodyStart + jsniStart + JSNI_BLOCK_START.length(); - int srcEnd = bodyStart + jsniEnd; - return new Interval(srcStart, srcEnd); + return jsTry + generateJavaScriptForHostedMode(func.getBody()) + jsCatch; } /** * Returns a string representing the source output of the JsNode, where all * JSNI idents have been replaced with legal JavaScript for hosted mode. */ - public static String generateJavaScriptForHostedMode(JsNode<?> node) { + private static String generateJavaScriptForHostedMode(JsNode<?> node) { DefaultTextOutput out = new DefaultTextOutput(false); JsSourceGenWithJsniIdentFixup vi = new JsSourceGenWithJsniIdentFixup(out); vi.accept(node); return out.toString(); } - /** - * Gets a unique name for this method and its signature (this is used to - * determine whether one method overrides another). - */ - public static String getJsniSignature(JMethod method) { - return method.getEnclosingType().getQualifiedSourceName() + "::" - + getMemberSignature(method); - } - - /** - * Gets a unique name for this method and its signature (this is used to - * determine whether one method overrides another). - */ - public static String getMemberSignature(JMethod method) { - String name = method.getName(); - StringBuilder sb = new StringBuilder(); - sb.append(name); - sb.append("("); - JParameter[] params = method.getParameters(); - for (int i = 0; i < params.length; ++i) { - JParameter param = params[i]; - String typeSig = param.getType().getJNISignature(); - sb.append(typeSig); - } - sb.append(")"); - String result = sb.toString(); - return result; - } - - /** - * In other words, it can have <code>return</code> statements. - */ - public static JsBlock parseAsFunctionBody(TreeLogger logger, String js, - String location, int startLine) throws UnableToCompleteException { - // Wrap it in fake function and parse it. - js = "function(){ " + js + " }"; - - JsParser jsParser = new JsParser(); - JsProgram jsPgm = new JsProgram(); - StringReader r = new StringReader(js); - - try { - List<JsStatement> stmts = jsParser.parse(jsPgm.getScope(), r, startLine); - - // Rip the body out of the parsed function and attach the JavaScript - // AST to the method. - // - JsFunction fn = (JsFunction) ((JsExprStmt) stmts.get(0)).getExpression(); - return fn.getBody(); - } catch (IOException e) { - logger.log(TreeLogger.ERROR, "Error reading JavaScript source", e); - throw new UnableToCompleteException(); - } catch (JsParserException e) { - SourceDetail dtl = e.getSourceDetail(); - if (dtl != null) { - StringBuilder sb = new StringBuilder(); - sb.append(location); - sb.append("("); - sb.append(dtl.getLine()); - sb.append(", "); - sb.append(dtl.getLineOffset()); - sb.append("): "); - sb.append(e.getMessage()); - logger.log(TreeLogger.ERROR, sb.toString(), e); - throw new UnableToCompleteException(); - } else { - logger.log(TreeLogger.ERROR, "Error parsing JSNI source", e); - throw new UnableToCompleteException(); - } - } - } - }
diff --git a/dev/core/src/com/google/gwt/dev/util/Util.java b/dev/core/src/com/google/gwt/dev/util/Util.java index 16edda6..237a3c3 100644 --- a/dev/core/src/com/google/gwt/dev/util/Util.java +++ b/dev/core/src/com/google/gwt/dev/util/Util.java
@@ -30,6 +30,7 @@ import java.io.BufferedReader; import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; @@ -173,23 +174,16 @@ } /** - * Copies an input stream out to a file. Closes the input steam and output - * stream. + * Copies an input stream out to an output stream. Closes the input steam and + * output stream. */ public static void copy(TreeLogger logger, InputStream is, OutputStream os) throws UnableToCompleteException { try { - byte[] buf = new byte[8 * 1024]; - int i = 0; - while ((i = is.read(buf)) != -1) { - os.write(buf, 0, i); - } + copy(is, os); } catch (IOException e) { logger.log(TreeLogger.ERROR, "Error during copy", e); throw new UnableToCompleteException(); - } finally { - Utility.close(is); - Utility.close(os); } } @@ -540,16 +534,14 @@ * Give the developer a chance to see the in-memory source that failed. */ public static void maybeDumpSource(TreeLogger logger, String location, - char[] source, String typeName) { + String source, String typeName) { if (isCompilationUnitOnDisk(location)) { // Don't write another copy. return; } - TreeLogger branch = logger.branch(TreeLogger.ERROR, - "Compilation problem due to '" + location + "'", null); - if (!branch.isLoggable(TreeLogger.INFO)) { + if (!logger.isLoggable(TreeLogger.INFO)) { // Don't bother dumping source if they can't see the related message. return; } @@ -558,16 +550,14 @@ Throwable caught = null; try { tmpSrc = File.createTempFile(typeName, ".java"); - writeCharsAsFile(logger, tmpSrc, source); + writeStringAsFile(tmpSrc, source); String dumpPath = tmpSrc.getAbsolutePath(); - branch.log(TreeLogger.INFO, "See snapshot: " + dumpPath, null); + logger.log(TreeLogger.INFO, "See snapshot: " + dumpPath, null); return; } catch (IOException e) { caught = e; - } catch (UnableToCompleteException e) { - caught = e; } - branch.log(TreeLogger.INFO, "Unable to dump source to disk", caught); + logger.log(TreeLogger.INFO, "Unable to dump source to disk", caught); } public static byte[] readFileAsBytes(File file) { @@ -623,6 +613,22 @@ } /** + * Reads an entire input stream as String. Closes the input stream. + */ + public static String readStreamAsString(InputStream in) { + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(1024); + copy(in, out); + return out.toString(DEFAULT_ENCODING); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException( + "The JVM does not support the compiler's default encoding.", e); + } catch (IOException e) { + return null; + } + } + + /** * @return null if the file could not be read */ public static byte[] readURLAsBytes(URL url) { @@ -830,6 +836,14 @@ } /** + * Returns a String representing the character content of the bytes; the bytes + * must be encoded using the compiler's default encoding. + */ + public static String toString(byte[] bytes) { + return toString(bytes, DEFAULT_ENCODING); + } + + /** * Creates a string array from the contents of a collection. */ public static String[] toStringArray(Collection<String> coll) { @@ -1018,6 +1032,19 @@ return true; } + private static void copy(InputStream is, OutputStream os) throws IOException { + try { + byte[] buf = new byte[8 * 1024]; + int i; + while ((i = is.read(buf)) != -1) { + os.write(buf, 0, i); + } + } finally { + Utility.close(is); + Utility.close(os); + } + } + /** * Reads the specified number of bytes from the {@link InputStream}. *
diff --git a/dev/core/src/com/google/gwt/util/tools/Utility.java b/dev/core/src/com/google/gwt/util/tools/Utility.java index cf07319..dcc60b6 100644 --- a/dev/core/src/com/google/gwt/util/tools/Utility.java +++ b/dev/core/src/com/google/gwt/util/tools/Utility.java
@@ -201,7 +201,7 @@ */ public static String getFileFromClassPath(String partialPath) throws IOException { - InputStream in = Utility.class.getClassLoader().getResourceAsStream( + InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream( partialPath); try { if (in == null) { @@ -279,7 +279,8 @@ if (override == null) { String partialPath = Utility.class.getName().replace('.', '/').concat( ".class"); - URL url = Utility.class.getClassLoader().getResource(partialPath); + URL url = Thread.currentThread().getContextClassLoader().getResource( + partialPath); if (url != null && "jar".equals(url.getProtocol())) { String path = url.toString(); String jarPath = path.substring(path.indexOf("file:"),
diff --git a/dev/core/super/com/google/gwt/core/client/GWTBridge.java b/dev/core/super/com/google/gwt/core/client/GWTBridge.java new file mode 100644 index 0000000..005bece --- /dev/null +++ b/dev/core/super/com/google/gwt/core/client/GWTBridge.java
@@ -0,0 +1,29 @@ +/* + * Copyright 2008 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.core.client; + +/** + * When running in hosted mode, acts as a bridge from {@link GWT} into the + * hosted mode environment. + */ +public abstract class GWTBridge { + + public abstract <T> T create(Class<?> classLiteral); + + public abstract String getVersion(); + + public abstract void log(String message, Throwable e); +}
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/JArrayTypeTest.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/JArrayTypeTest.java index 90b5b94..6966b56 100644 --- a/dev/core/test/com/google/gwt/core/ext/typeinfo/JArrayTypeTest.java +++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/JArrayTypeTest.java
@@ -17,7 +17,6 @@ import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.core.ext.typeinfo.test.CA; import com.google.gwt.core.ext.typeinfo.test.CB; import com.google.gwt.core.ext.typeinfo.test.MyCustomList; import com.google.gwt.core.ext.typeinfo.test.MyList;
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/JClassTypeTest.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/JClassTypeTest.java index 462da59..7d385fc 100644 --- a/dev/core/test/com/google/gwt/core/ext/typeinfo/JClassTypeTest.java +++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/JClassTypeTest.java
@@ -17,19 +17,10 @@ import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.dev.jdt.StaticCompilationUnitProvider; -import com.google.gwt.dev.jdt.TypeOracleBuilder; -import com.google.gwt.dev.jdt.URLCompilationUnitProvider; import com.google.gwt.dev.util.log.PrintWriterTreeLogger; import junit.framework.TestCase; -import java.io.File; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; - /** * Tests related to JClassType. See individual test methods to details. */ @@ -43,7 +34,6 @@ } public void testGetOverridableMethods() throws TypeOracleException { - TreeLogger logger = TreeLogger.NULL; TypeOracle typeOracle = moduleContext.getOracle(); // TypeOracle typeOracle = buildOracleFromTestPackage(logger); @@ -223,34 +213,6 @@ } } - private void addCompilationUnitsInPath(TypeOracleBuilder builder, - File sourcePathEntry, String pkgName) throws UnableToCompleteException, - MalformedURLException { - File pkgPath = new File(sourcePathEntry, pkgName.replace('.', '/')); - File[] files = pkgPath.listFiles(); - if (files == null) { - // No files found. - return; - } - - for (int i = 0; i < files.length; i++) { - File file = files[i]; - if (file.isFile()) { - // If it's a source file, slurp it in. - if (file.getName().endsWith(".java")) { - URL location = file.toURL(); - CompilationUnitProvider cup = new URLCompilationUnitProvider( - location, pkgName); - builder.addCompilationUnit(cup); - } - } else { - // Recurse into subpackages. - addCompilationUnitsInPath(builder, sourcePathEntry, pkgName - + file.getName()); - } - } - } - private void assertMethodNotOverridable(TypeOracle typeOracle, String expectedTypeName, String searchTypeName, String methodName, String[] paramTypeNames) throws TypeOracleException {
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/JDelegatingClassTypeTestBase.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/JDelegatingClassTypeTestBase.java index d00e716..2c3e989 100644 --- a/dev/core/test/com/google/gwt/core/ext/typeinfo/JDelegatingClassTypeTestBase.java +++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/JDelegatingClassTypeTestBase.java
@@ -398,17 +398,6 @@ /** * Test method for - * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getCompilationUnit()}. - */ - public void testGetCompilationUnit() throws NotFoundException { - JDelegatingClassType testType = getTestType(); - JClassType baseType = testType.getBaseType(); - - assertEquals(testType.getCompilationUnit(), baseType.getCompilationUnit()); - } - - /** - * Test method for * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getConstructor(com.google.gwt.core.ext.typeinfo.JType[])}. */ public void testGetConstructor() throws NotFoundException {
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/JEnumTypeTest.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/JEnumTypeTest.java index 7ec4612..f10d243 100644 --- a/dev/core/test/com/google/gwt/core/ext/typeinfo/JEnumTypeTest.java +++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/JEnumTypeTest.java
@@ -18,8 +18,7 @@ import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.test.MyEnum; -import com.google.gwt.dev.cfg.ModuleDef; -import com.google.gwt.dev.cfg.ModuleDefLoader; +import com.google.gwt.dev.util.log.PrintWriterTreeLogger; import junit.framework.TestCase; @@ -27,18 +26,18 @@ * Tests for {@link JEnumType}. */ public class JEnumTypeTest extends TestCase { - private final TreeLogger logger = TreeLogger.NULL; - private ModuleDef moduleDef; + private final boolean logToConsole = false; - private final TypeOracle typeOracle; + private final ModuleContext moduleContext = new ModuleContext(logToConsole + ? new PrintWriterTreeLogger() : TreeLogger.NULL, + "com.google.gwt.core.ext.typeinfo.TypeOracleTest"); - public JEnumTypeTest() throws UnableToCompleteException, NotFoundException { - moduleDef = ModuleDefLoader.loadFromClassPath(logger, - "com.google.gwt.core.ext.typeinfo.TypeOracleTest"); - typeOracle = moduleDef.getTypeOracle(logger); + public JEnumTypeTest() throws UnableToCompleteException { } + private final TypeOracle typeOracle = moduleContext.getOracle(); + /** * Test method for * {@link com.google.gwt.core.ext.typeinfo.JEnumType#getEnumConstants()}.
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/ModuleContext.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/ModuleContext.java index acb8b2f..51ac681 100644 --- a/dev/core/test/com/google/gwt/core/ext/typeinfo/ModuleContext.java +++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/ModuleContext.java
@@ -21,7 +21,7 @@ import com.google.gwt.dev.cfg.ModuleDefLoader; /** - * Helper for loading modules from the classpath. + * Helper for loading modules from the classpath. */ class ModuleContext { private final TypeOracle oracle; @@ -32,12 +32,8 @@ moduleDef = ModuleDefLoader.loadFromClassPath(logger, moduleName); oracle = moduleDef.getTypeOracle(logger); } - + public TypeOracle getOracle() { return oracle; } - - public ModuleDef getModule() { - return moduleDef; - } } \ No newline at end of file
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/TypeOracleAnnotationSupportTest.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/TypeOracleAnnotationSupportTest.java index f9b6f16..a6ddb67 100644 --- a/dev/core/test/com/google/gwt/core/ext/typeinfo/TypeOracleAnnotationSupportTest.java +++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/TypeOracleAnnotationSupportTest.java
@@ -24,8 +24,7 @@ import com.google.gwt.core.ext.typeinfo.test.PrimitivesAnnotatedClass; import com.google.gwt.core.ext.typeinfo.test.SourceRetentionAnnotation; import com.google.gwt.core.ext.typeinfo.test.TestAnnotation; -import com.google.gwt.dev.cfg.ModuleDef; -import com.google.gwt.dev.cfg.ModuleDefLoader; +import com.google.gwt.dev.util.log.PrintWriterTreeLogger; import junit.framework.TestCase; @@ -89,15 +88,14 @@ testAnnotation.emptyArray().length); } - private final TreeLogger logger = TreeLogger.NULL; - private ModuleDef moduleDef; + private final boolean logToConsole = false; + private final ModuleContext moduleContext = new ModuleContext(logToConsole + ? new PrintWriterTreeLogger() : TreeLogger.NULL, + "com.google.gwt.core.ext.typeinfo.TypeOracleTest"); - private final TypeOracle typeOracle; + private final TypeOracle typeOracle = moduleContext.getOracle(); public TypeOracleAnnotationSupportTest() throws UnableToCompleteException { - moduleDef = ModuleDefLoader.loadFromClassPath(logger, - "com.google.gwt.core.ext.typeinfo.TypeOracleTest"); - typeOracle = moduleDef.getTypeOracle(logger); } /**
diff --git a/dev/core/test/com/google/gwt/dev/jdt/BinaryTypeReferenceRestrictionsCheckerTest.java b/dev/core/test/com/google/gwt/dev/javac/BinaryTypeReferenceRestrictionsCheckerTest.java similarity index 96% rename from dev/core/test/com/google/gwt/dev/jdt/BinaryTypeReferenceRestrictionsCheckerTest.java rename to dev/core/test/com/google/gwt/dev/javac/BinaryTypeReferenceRestrictionsCheckerTest.java index 5d60966..16b7e90 100644 --- a/dev/core/test/com/google/gwt/dev/jdt/BinaryTypeReferenceRestrictionsCheckerTest.java +++ b/dev/core/test/com/google/gwt/dev/javac/BinaryTypeReferenceRestrictionsCheckerTest.java
@@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.google.gwt.dev.jdt; +package com.google.gwt.dev.javac; -import com.google.gwt.dev.jdt.BinaryTypeReferenceRestrictionsChecker.BinaryTypeReferenceSite; +import com.google.gwt.dev.javac.BinaryTypeReferenceRestrictionsChecker.BinaryTypeReferenceSite; import junit.framework.TestCase; @@ -159,7 +159,7 @@ * annotation and in a local variable declaration. It then checks that the we * find all of these locations except for the one used in an annotation. */ - public void testFindInvalidBinaryTypeReferenceSites() { + public void testFindAllBinaryTypeReferenceSites() { CompilationResult compilationResult = new CompilationResult( "TestCompilationUnit.java".toCharArray(), 0, 0, 0); CompilationUnitDeclaration cud = new CompilationUnitDeclaration(null, @@ -201,7 +201,7 @@ typeDeclaration.superclass, methodDeclaration.returnType, localDeclaration.type}; - List<BinaryTypeReferenceSite> binaryTypeReferenceSites = BinaryTypeReferenceRestrictionsChecker.findInvalidBinaryTypeReferenceSites(cud); + List<BinaryTypeReferenceSite> binaryTypeReferenceSites = BinaryTypeReferenceRestrictionsChecker.findAllBinaryTypeReferenceSites(cud); assertEquals(expectedExpressions.length, binaryTypeReferenceSites.size()); for (int i = 0; i < binaryTypeReferenceSites.size(); ++i) { BinaryTypeReferenceSite binaryTypeReferenceSite = binaryTypeReferenceSites.get(i);
diff --git a/dev/core/test/com/google/gwt/dev/javac/CompilationStateTest.java b/dev/core/test/com/google/gwt/dev/javac/CompilationStateTest.java new file mode 100644 index 0000000..23cdb49 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/javac/CompilationStateTest.java
@@ -0,0 +1,228 @@ +/* + * Copyright 2008 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.TreeLogger; +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.dev.javac.CompilationUnit.State; +import com.google.gwt.dev.javac.impl.MockJavaSourceFile; +import com.google.gwt.dev.javac.impl.SourceFileCompilationUnit; +import com.google.gwt.dev.util.log.AbstractTreeLogger; +import com.google.gwt.dev.util.log.PrintWriterTreeLogger; + +import junit.framework.TestCase; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +/** + * Tests {@link CompilationState}. + */ +public class CompilationStateTest extends TestCase { + + private MockJavaSourceOracle oracle = new MockJavaSourceOracle( + JavaSourceCodeBase.getStandardResources()); + + private CompilationState state = new CompilationState(oracle); + + public void testAddGeneratedCompilationUnit() { + validateCompilationState(); + + // Add a unit and ensure it shows up. + addGeneratedUnit(JavaSourceCodeBase.FOO); + validateCompilationState(JavaSourceCodeBase.FOO.getTypeName()); + + // Ensure it disappears after a refresh. + state.refresh(); + validateCompilationState(); + } + + static void assertUnitsChecked(Collection<CompilationUnit> units) { + for (CompilationUnit unit : units) { + assertSame(State.CHECKED, unit.getState()); + assertNull(unit.getErrors()); + assertTrue(unit.getCompiledClasses().size() > 0); + } + } + + public void testCompile() throws UnableToCompleteException { + validateUncompiled(); + state.compile(createTreeLogger()); + assertUnitsChecked(state.getCompilationUnits()); + } + + public void testCompileError() throws UnableToCompleteException { + oracle.add(JavaSourceCodeBase.BAR); + state.refresh(); + validateUncompiled(); + state.compile(createTreeLogger()); + + CompilationUnit badUnit = state.getCompilationUnitMap().get( + JavaSourceCodeBase.BAR.getTypeName()); + assertSame(State.ERROR, badUnit.getState()); + + Set<CompilationUnit> goodUnits = new HashSet<CompilationUnit>( + state.getCompilationUnits()); + goodUnits.remove(badUnit); + assertUnitsChecked(goodUnits); + } + + public void testCompileWithGeneratedUnits() throws UnableToCompleteException { + validateUncompiled(); + state.compile(createTreeLogger()); + assertUnitsChecked(state.getCompilationUnits()); + addGeneratedUnit(JavaSourceCodeBase.FOO); + state.compile(createTreeLogger()); + assertUnitsChecked(state.getCompilationUnits()); + } + + public void testCompileWithGeneratedUnitsError() + throws UnableToCompleteException { + validateUncompiled(); + state.compile(createTreeLogger()); + assertUnitsChecked(state.getCompilationUnits()); + addGeneratedUnit(JavaSourceCodeBase.BAR); + state.compile(createTreeLogger()); + + CompilationUnit badUnit = state.getCompilationUnitMap().get( + JavaSourceCodeBase.BAR.getTypeName()); + assertSame(State.ERROR, badUnit.getState()); + + Set<CompilationUnit> goodUnits = new HashSet<CompilationUnit>( + state.getCompilationUnits()); + goodUnits.remove(badUnit); + assertUnitsChecked(goodUnits); + } + + public void testSourceOracleAdd() { + validateCompilationState(); + + int size = state.getCompilationUnits().size(); + oracle.add(JavaSourceCodeBase.FOO); + state.refresh(); + assertEquals(size + 1, state.getCompilationUnits().size()); + validateCompilationState(); + } + + public void testSourceOracleBasic() { + validateCompilationState(); + } + + public void testSourceOracleEmpty() { + oracle = new MockJavaSourceOracle(); + state = new CompilationState(oracle); + validateCompilationState(); + } + + public void testSourceOracleRemove() { + validateCompilationState(); + + int size = state.getCompilationUnits().size(); + oracle.remove(JavaSourceCodeBase.OBJECT.getTypeName()); + state.refresh(); + assertEquals(size - 1, state.getCompilationUnits().size()); + validateCompilationState(); + } + + public void testSourceOracleReplace() { + validateCompilationState(); + + int size = state.getCompilationUnits().size(); + oracle.replace(new MockJavaSourceFile(JavaSourceCodeBase.OBJECT)); + state.refresh(); + assertEquals(size, state.getCompilationUnits().size()); + validateCompilationState(); + } + + public void testSourceOracleReplaceWithSame() { + validateCompilationState(); + + int size = state.getCompilationUnits().size(); + oracle.replace(JavaSourceCodeBase.OBJECT); + state.refresh(); + assertEquals(size, state.getCompilationUnits().size()); + validateCompilationState(); + } + + private void addGeneratedUnit(JavaSourceFile sourceFile) { + state.addGeneratedCompilationUnit(new SourceFileCompilationUnit(sourceFile) { + @Override + public boolean isGenerated() { + return true; + } + }); + } + + private void validateCompilationState(String... generatedTypeNames) { + // Save off the reflected collections. + Map<String, CompilationUnit> unitMap = state.getCompilationUnitMap(); + Set<CompilationUnit> units = state.getCompilationUnits(); + + // Validate that the collections are consistent with each other. + assertEquals(new HashSet<CompilationUnit>(unitMap.values()), units); + + // Save off a mutable copy of the source map and generated types to compare. + Map<String, JavaSourceFile> sourceMap = new HashMap<String, JavaSourceFile>( + oracle.getSourceMap()); + Set<String> generatedTypes = new HashSet<String>( + Arrays.asList(generatedTypeNames)); + assertEquals(sourceMap.size() + generatedTypes.size(), units.size()); + for (Entry<String, CompilationUnit> entry : unitMap.entrySet()) { + // Validate source file internally consistent. + String className = entry.getKey(); + CompilationUnit unit = entry.getValue(); + assertEquals(className, unit.getTypeName()); + + // Find the matching resource (and remove it). + if (unit.isGenerated()) { + assertTrue(generatedTypes.contains(className)); + assertNotNull(generatedTypes.remove(className)); + } else { + assertTrue(sourceMap.containsKey(className)); + // TODO: Validate the source file matches the resource. + assertNotNull(sourceMap.remove(className)); + } + } + // The mutable sets should be empty now. + assertEquals(0, sourceMap.size()); + assertEquals(0, generatedTypes.size()); + } + + private void validateUncompiled() { + for (CompilationUnit unit : state.getCompilationUnits()) { + assertNull(unit.getJdtCud()); + } + } + + /** + * Tweak this if you want to see the log output. + */ + private TreeLogger createTreeLogger() { + boolean reallyLog = false; + if (reallyLog) { + AbstractTreeLogger logger = new PrintWriterTreeLogger(); + logger.setMaxDetail(TreeLogger.ALL); + return logger; + } else { + return TreeLogger.NULL; + } + } +}
diff --git a/dev/core/test/com/google/gwt/dev/jdt/GWTProblemTest.java b/dev/core/test/com/google/gwt/dev/javac/GWTProblemTest.java similarity index 97% rename from dev/core/test/com/google/gwt/dev/jdt/GWTProblemTest.java rename to dev/core/test/com/google/gwt/dev/javac/GWTProblemTest.java index 301bcba..865a490 100644 --- a/dev/core/test/com/google/gwt/dev/jdt/GWTProblemTest.java +++ b/dev/core/test/com/google/gwt/dev/javac/GWTProblemTest.java
@@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.google.gwt.dev.jdt; +package com.google.gwt.dev.javac; import com.google.gwt.core.ext.TreeLogger.HelpInfo;
diff --git a/dev/core/test/com/google/gwt/dev/jdt/JSORestrictionsTest.java b/dev/core/test/com/google/gwt/dev/javac/JSORestrictionsTest.java similarity index 90% rename from dev/core/test/com/google/gwt/dev/jdt/JSORestrictionsTest.java rename to dev/core/test/com/google/gwt/dev/javac/JSORestrictionsTest.java index ac2d451..ce0305d 100644 --- a/dev/core/test/com/google/gwt/dev/jdt/JSORestrictionsTest.java +++ b/dev/core/test/com/google/gwt/dev/javac/JSORestrictionsTest.java
@@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.google.gwt.dev.jdt; +package com.google.gwt.dev.javac; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; @@ -42,7 +42,7 @@ buggyCode.append(" int myStsate = 3;\n"); buggyCode.append("}\n"); - shouldGenerateError(buggyCode, "Line 4: " + shouldGenerateError(buggyCode, "Line 4: " + JSORestrictionsChecker.ERR_INSTANCE_FIELD); } @@ -53,7 +53,7 @@ buggyCode.append(" protected Buggy(int howBuggy) { }\n"); buggyCode.append("}\n"); - shouldGenerateError(buggyCode, "Line 3: " + shouldGenerateError(buggyCode, "Line 3: " + JSORestrictionsChecker.ERR_CONSTRUCTOR_WITH_PARAMETERS); } @@ -67,7 +67,7 @@ buggyCode.append(" MyJSO makeOne() { return new MyJSO(); }\n"); buggyCode.append("}\n"); - shouldGenerateError(buggyCode, "Line 6: " + shouldGenerateError(buggyCode, "Line 6: " + JSORestrictionsChecker.ERR_NEW_JSO); } @@ -78,7 +78,7 @@ buggyCode.append("}\n"); // The public constructor is implicit. - shouldGenerateError(buggyCode, "Line 2: " + shouldGenerateError(buggyCode, "Line 2: " + JSORestrictionsChecker.ERR_NONPROTECTED_CONSTRUCTOR); } @@ -95,7 +95,7 @@ buggyCode.append(" }\n"); buggyCode.append("}\n"); - shouldGenerateError(buggyCode, "Line 6: " + shouldGenerateError(buggyCode, "Line 6: " + JSORestrictionsChecker.errInterfaceWithMethods("Buggy.Squeaks")); } @@ -106,7 +106,7 @@ buggyCode.append(" protected Buggy() { while(true) { } }\n"); buggyCode.append("}\n"); - shouldGenerateError(buggyCode, "Line 3: " + shouldGenerateError(buggyCode, "Line 3: " + JSORestrictionsChecker.ERR_NONEMPTY_CONSTRUCTOR); } @@ -118,7 +118,7 @@ buggyCode.append(" protected Buggy() { }\n"); buggyCode.append("}\n"); - shouldGenerateError(buggyCode, "Line 3: " + shouldGenerateError(buggyCode, "Line 3: " + JSORestrictionsChecker.ERR_INSTANCE_METHOD_NONFINAL); } @@ -129,7 +129,7 @@ buggyCode.append(" Buggy() { }\n"); buggyCode.append("}\n"); - shouldGenerateError(buggyCode, "Line 3: " + shouldGenerateError(buggyCode, "Line 3: " + JSORestrictionsChecker.ERR_NONPROTECTED_CONSTRUCTOR); } @@ -142,7 +142,7 @@ buggyCode.append(" }\n"); buggyCode.append("}\n"); - shouldGenerateError(buggyCode, "Line 3: " + shouldGenerateError(buggyCode, "Line 3: " + JSORestrictionsChecker.ERR_IS_NONSTATIC_NESTED); } @@ -154,7 +154,7 @@ buggyCode.append(" public final Object clone() { return this; }\n"); buggyCode.append("}\n"); - shouldGenerateError(buggyCode, "Line 4: " + shouldGenerateError(buggyCode, "Line 4: " + JSORestrictionsChecker.ERR_OVERRIDDEN_METHOD); } @@ -179,13 +179,13 @@ UnitTestTreeLogger.Builder builder = new UnitTestTreeLogger.Builder(); builder.setLowestLogLevel(TreeLogger.ERROR); if (expectedError != null) { - builder.expectError("Errors in \'transient source for Buggy\'", null); + builder.expectError("Errors in \'/mock/Buggy\'", null); builder.expectError(expectedError, null); - builder.expectError( - "Compilation problem due to \'transient source for Buggy\'", null); } UnitTestTreeLogger logger = builder.createLogger(); - TypeOracleTestingUtils.buildTypeOracleForCode("Buggy", buggyCode, logger); + CompilationUnit buggyCup = new MockCompilationUnit("Buggy", + buggyCode.toString()); + TypeOracleTestingUtils.buildStandardTypeOracleWith(logger, buggyCup); logger.assertCorrectLogEntries(); }
diff --git a/dev/core/test/com/google/gwt/dev/javac/JavaCompilationSuite.java b/dev/core/test/com/google/gwt/dev/javac/JavaCompilationSuite.java new file mode 100644 index 0000000..1505a36 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/javac/JavaCompilationSuite.java
@@ -0,0 +1,43 @@ +/* + * Copyright 2008 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.dev.javac.impl.JavaSourceOracleImplTest; +import com.google.gwt.dev.javac.impl.JdtBehaviorTest; + +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * Tests script and resource injection. + */ +public class JavaCompilationSuite { + public static Test suite() { + TestSuite suite = new TestSuite(JavaCompilationSuite.class.getName()); + + suite.addTestSuite(BinaryTypeReferenceRestrictionsCheckerTest.class); + suite.addTestSuite(CompilationStateTest.class); + suite.addTestSuite(GWTProblemTest.class); + suite.addTestSuite(JdtBehaviorTest.class); + suite.addTestSuite(JdtCompilerTest.class); + suite.addTestSuite(JSORestrictionsTest.class); + suite.addTestSuite(JavaSourceOracleImplTest.class); + suite.addTestSuite(LongFromJSNITest.class); + suite.addTestSuite(TypeOracleMediatorTest.class); + + return suite; + } +}
diff --git a/dev/core/test/com/google/gwt/dev/javac/JavaSourceCodeBase.java b/dev/core/test/com/google/gwt/dev/javac/JavaSourceCodeBase.java new file mode 100644 index 0000000..d3679d9 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/javac/JavaSourceCodeBase.java
@@ -0,0 +1,52 @@ +/* + * Copyright 2008 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.dev.javac.impl.JavaResourceBase; +import com.google.gwt.dev.javac.impl.MockJavaSourceFile; + +/** + * Contains standard Java source files for testing. + */ +public class JavaSourceCodeBase { + + public static final MockJavaSourceFile ANNOTATION = new MockJavaSourceFile( + JavaResourceBase.ANNOTATION); + public static final MockJavaSourceFile BAR = new MockJavaSourceFile( + JavaResourceBase.BAR); + public static final MockJavaSourceFile CLASS = new MockJavaSourceFile( + JavaResourceBase.CLASS); + public static final MockJavaSourceFile FOO = new MockJavaSourceFile( + JavaResourceBase.FOO); + public static final MockJavaSourceFile JAVASCRIPTOBJECT = new MockJavaSourceFile( + JavaResourceBase.JAVASCRIPTOBJECT); + public static final MockJavaSourceFile MAP = new MockJavaSourceFile( + JavaResourceBase.MAP); + public static final MockJavaSourceFile OBJECT = new MockJavaSourceFile( + JavaResourceBase.OBJECT); + public static final MockJavaSourceFile SERIALIZABLE = new MockJavaSourceFile( + JavaResourceBase.SERIALIZABLE); + public static final MockJavaSourceFile STRING = new MockJavaSourceFile( + JavaResourceBase.STRING); + public static final MockJavaSourceFile SUPPRESS_WARNINGS = new MockJavaSourceFile( + JavaResourceBase.SUPPRESS_WARNINGS); + + public static MockJavaSourceFile[] getStandardResources() { + return new MockJavaSourceFile[] { + ANNOTATION, CLASS, JAVASCRIPTOBJECT, MAP, OBJECT, SERIALIZABLE, STRING, + SUPPRESS_WARNINGS}; + } +}
diff --git a/dev/core/test/com/google/gwt/dev/javac/JdtCompilerTest.java b/dev/core/test/com/google/gwt/dev/javac/JdtCompilerTest.java new file mode 100644 index 0000000..539abe7 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/javac/JdtCompilerTest.java
@@ -0,0 +1,85 @@ +/* + * Copyright 2008 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.dev.javac.impl.SourceFileCompilationUnit; + +import junit.framework.TestCase; + +import org.eclipse.jdt.internal.compiler.CompilationResult; +import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Test class for {@link JdtCompiler}. + */ +public class JdtCompilerTest extends TestCase { + + static void assertUnitsCompiled(Collection<CompilationUnit> units) { + for (CompilationUnit unit : units) { + CompilationUnitDeclaration cud = unit.getJdtCud(); + assertFalse(cud.hasErrors()); + CompilationResult result = cud.compilationResult(); + assertFalse(result.hasProblems()); + assertTrue(result.getClassFiles().length > 0); + } + } + + static void assertUnitHasErrors(CompilationUnit unit, int numErrors) { + CompilationUnitDeclaration cud = unit.getJdtCud(); + CompilationResult result = cud.compilationResult(); + assertTrue(result.hasErrors()); + assertEquals(numErrors, result.getErrors().length); + assertTrue(result.getClassFiles().length > 0); + } + + public void testCompile() { + List<CompilationUnit> units = new ArrayList<CompilationUnit>(); + addAll(units, JavaSourceCodeBase.getStandardResources()); + addAll(units, JavaSourceCodeBase.FOO, JavaSourceCodeBase.BAR); + JdtCompiler.compile(units); + assertUnitsCompiled(units); + } + + public void testCompileError() { + List<CompilationUnit> units = new ArrayList<CompilationUnit>(); + addAll(units, JavaSourceCodeBase.getStandardResources()); + addAll(units, JavaSourceCodeBase.BAR); + JdtCompiler.compile(units); + assertUnitsCompiled(units.subList(0, units.size() - 1)); + assertUnitHasErrors(units.get(units.size() - 1), 1); + } + + public void testCompileIncremental() { + List<CompilationUnit> units = new ArrayList<CompilationUnit>(); + addAll(units, JavaSourceCodeBase.getStandardResources()); + JdtCompiler.compile(units); + assertUnitsCompiled(units); + addAll(units, JavaSourceCodeBase.FOO, JavaSourceCodeBase.BAR); + JdtCompiler.compile(units); + assertUnitsCompiled(units); + } + + private void addAll(Collection<CompilationUnit> units, + JavaSourceFile... sourceFiles) { + for (JavaSourceFile sourceFile : sourceFiles) { + units.add(new SourceFileCompilationUnit(sourceFile)); + } + } +}
diff --git a/dev/core/test/com/google/gwt/dev/jdt/LongFromJSNITest.java b/dev/core/test/com/google/gwt/dev/javac/LongFromJSNITest.java similarity index 86% rename from dev/core/test/com/google/gwt/dev/jdt/LongFromJSNITest.java rename to dev/core/test/com/google/gwt/dev/javac/LongFromJSNITest.java index 4bb445c..0269e7b 100644 --- a/dev/core/test/com/google/gwt/dev/jdt/LongFromJSNITest.java +++ b/dev/core/test/com/google/gwt/dev/javac/LongFromJSNITest.java
@@ -13,32 +13,23 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.google.gwt.dev.jdt; +package com.google.gwt.dev.javac; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.TypeOracle; +import com.google.gwt.dev.javac.CompilationUnit; import com.google.gwt.dev.util.UnitTestTreeLogger; import junit.framework.TestCase; +import java.util.HashSet; +import java.util.Set; + /** * Test access to longs from JSNI. */ public class LongFromJSNITest extends TestCase { - public void testBogusRef() throws UnableToCompleteException { - StringBuffer code = new StringBuffer(); - code.append("class Buggy { \n"); - code.append("volatile long x = -1; \n"); - code.append("native void jsniMeth() /*-{ \n"); - code.append(" // @\\bogus refs should just be skipped \n"); - code.append(" $wnd.alert(\"x is: \"+this.@Buggy::x); }-*/; \n"); - code.append("} \n"); - - shouldGenerateError(code, 3, - "Referencing field 'Buggy.x': type 'long' is not safe to access in JSNI code"); - } - public void testCyclicReferences() throws UnableToCompleteException { { StringBuffer buggy = new StringBuffer(); @@ -204,19 +195,6 @@ "Referencing method 'Buggy.m': return type 'long' is not safe to access in JSNI code"); } - public void testRefInString() throws UnableToCompleteException { - { - StringBuffer code = new StringBuffer(); - code.append("import com.google.gwt.core.client.UnsafeNativeLong;"); - code.append("class Buggy {\n"); - code.append(" void print(long x) { }\n"); - code.append(" native void jsniMeth() /*-{ 'this.@Buggy::print(J)(0)'; }-*/;\n"); - code.append("}\n"); - - shouldGenerateNoError(code); - } - } - public void testUnsafeAnnotation() throws UnableToCompleteException { { StringBuffer code = new StringBuffer(); @@ -231,6 +209,19 @@ } } + public void testRefInString() throws UnableToCompleteException { + { + StringBuffer code = new StringBuffer(); + code.append("import com.google.gwt.core.client.UnsafeNativeLong;"); + code.append("class Buggy {\n"); + code.append(" void print(long x) { }\n"); + code.append(" native void jsniMeth() /*-{ 'this.@Buggy::print(J)(0)'; }-*/;\n"); + code.append("}\n"); + + shouldGenerateNoError(code); + } + } + public void testViolator() throws UnableToCompleteException { { StringBuffer okay = new StringBuffer(); @@ -273,30 +264,26 @@ } } - private void addLongCheckingCups(TypeOracleBuilder builder) - throws UnableToCompleteException { - { - StringBuilder code = new StringBuilder(); - code.append("package com.google.gwt.core.client;\n"); - code.append("public @interface UnsafeNativeLong {\n"); - code.append("}\n"); - - TypeOracleTestingUtils.addCup(builder, - "com.google.gwt.core.client.UnsafeNativeLong", code); - } + private void addLongCheckingCups(Set<CompilationUnit> units) { + StringBuilder code = new StringBuilder(); + code.append("package com.google.gwt.core.client;\n"); + code.append("public @interface UnsafeNativeLong {\n"); + code.append("}\n"); + units.add(new MockCompilationUnit( + "com.google.gwt.core.client.UnsafeNativeLong", code.toString())); } private TypeOracle buildOracle(CharSequence buggyCode, CharSequence extraCode, UnitTestTreeLogger logger) throws UnableToCompleteException { - TypeOracleBuilder builder = new TypeOracleBuilder(); - TypeOracleTestingUtils.addStandardCups(builder); - addLongCheckingCups(builder); - TypeOracleTestingUtils.addCup(builder, "Buggy", buggyCode); + Set<CompilationUnit> units = new HashSet<CompilationUnit>(); + addLongCheckingCups(units); + units.add(new MockCompilationUnit("Buggy", buggyCode.toString())); if (extraCode != null) { - TypeOracleTestingUtils.addCup(builder, "Extra", extraCode); + units.add(new MockCompilationUnit("Extra", extraCode.toString())); } - return builder.build(logger); + return TypeOracleTestingUtils.buildStandardTypeOracleWith(logger, + units.toArray(new CompilationUnit[units.size()])); } private void shouldGenerateError(CharSequence buggyCode, @@ -305,11 +292,9 @@ UnitTestTreeLogger.Builder b = new UnitTestTreeLogger.Builder(); b.setLowestLogLevel(TreeLogger.ERROR); if (message != null) { - b.expect(TreeLogger.ERROR, "Errors in 'transient source for Buggy'", null); - final String fullMessage = "Line " + line + ": " + message; + b.expect(TreeLogger.ERROR, "Errors in '/mock/Buggy'", null); + final String fullMessage = "Line " + line + ": " + message; b.expect(TreeLogger.ERROR, fullMessage, null); - b.expect(TreeLogger.ERROR, - "Compilation problem due to 'transient source for Buggy'", null); } UnitTestTreeLogger logger = b.createLogger(); TypeOracle oracle = buildOracle(buggyCode, extraCode, logger);
diff --git a/dev/core/test/com/google/gwt/dev/javac/MockCompilationUnit.java b/dev/core/test/com/google/gwt/dev/javac/MockCompilationUnit.java new file mode 100644 index 0000000..9f07db9 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/javac/MockCompilationUnit.java
@@ -0,0 +1,50 @@ +/* + * Copyright 2008 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; + +public class MockCompilationUnit extends CompilationUnit { + + private final String typeName; + private final String source; + + public MockCompilationUnit(String typeName) { + this.typeName = typeName; + this.source = null; + } + + public MockCompilationUnit(String typeName, String source) { + this.typeName = typeName; + this.source = source; + } + + public String getDisplayLocation() { + return "/mock/" + getTypeName(); + } + + @Override + public String getSource() { + assert source != null; + return source; + } + + public String getTypeName() { + return typeName; + } + + public boolean isGenerated() { + return true; + } +} \ No newline at end of file
diff --git a/dev/core/test/com/google/gwt/dev/javac/MockJavaSourceOracle.java b/dev/core/test/com/google/gwt/dev/javac/MockJavaSourceOracle.java new file mode 100644 index 0000000..bb77e8c --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/javac/MockJavaSourceOracle.java
@@ -0,0 +1,92 @@ +/* + * Copyright 2008 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.dev.javac.JavaSourceFile; +import com.google.gwt.dev.javac.JavaSourceOracle; + +import junit.framework.Assert; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * A simple {@link ResourceOracle} for testing. + */ +public class MockJavaSourceOracle implements JavaSourceOracle { + + private Map<String, JavaSourceFile> exportedMap = Collections.emptyMap(); + private Set<JavaSourceFile> exportedValues = Collections.emptySet(); + + public MockJavaSourceOracle(JavaSourceFile... sourceFiles) { + add(sourceFiles); + } + + public Set<String> getClassNames() { + return exportedMap.keySet(); + } + + public Set<JavaSourceFile> getSourceFiles() { + return exportedValues; + } + + public Map<String, JavaSourceFile> getSourceMap() { + return exportedMap; + } + + void add(JavaSourceFile... sourceFiles) { + Map<String, JavaSourceFile> newMap = new HashMap<String, JavaSourceFile>( + exportedMap); + for (JavaSourceFile sourceFile : sourceFiles) { + String className = sourceFile.getTypeName(); + Assert.assertFalse(newMap.containsKey(className)); + newMap.put(className, sourceFile); + } + export(newMap); + } + + void remove(String... classNames) { + Map<String, JavaSourceFile> newMap = new HashMap<String, JavaSourceFile>( + exportedMap); + for (String className : classNames) { + JavaSourceFile oldValue = newMap.remove(className); + Assert.assertNotNull(oldValue); + } + export(newMap); + } + + void replace(JavaSourceFile... sourceFiles) { + Map<String, JavaSourceFile> newMap = new HashMap<String, JavaSourceFile>( + exportedMap); + for (JavaSourceFile sourceFile : sourceFiles) { + String className = sourceFile.getTypeName(); + Assert.assertTrue(newMap.containsKey(className)); + newMap.put(className, sourceFile); + } + export(newMap); + } + + private void export(Map<String, JavaSourceFile> newMap) { + exportedMap = Collections.unmodifiableMap(newMap); + // Make a new hash set for constant lookup. + exportedValues = Collections.unmodifiableSet(new HashSet<JavaSourceFile>( + exportedMap.values())); + } + +} \ No newline at end of file
diff --git a/dev/core/test/com/google/gwt/dev/jdt/TypeOracleBuilderTest.java b/dev/core/test/com/google/gwt/dev/javac/TypeOracleMediatorTest.java similarity index 62% rename from dev/core/test/com/google/gwt/dev/jdt/TypeOracleBuilderTest.java rename to dev/core/test/com/google/gwt/dev/javac/TypeOracleMediatorTest.java index 02599d1..7218d07 100644 --- a/dev/core/test/com/google/gwt/dev/jdt/TypeOracleBuilderTest.java +++ b/dev/core/test/com/google/gwt/dev/javac/TypeOracleMediatorTest.java
@@ -13,11 +13,10 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.google.gwt.dev.jdt; +package com.google.gwt.dev.javac; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider; import com.google.gwt.core.ext.typeinfo.JArrayType; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.JField; @@ -27,114 +26,34 @@ import com.google.gwt.core.ext.typeinfo.NotFoundException; import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.core.ext.typeinfo.TypeOracleException; +import com.google.gwt.dev.javac.CompilationUnit.State; +import com.google.gwt.dev.javac.impl.Shared; import com.google.gwt.dev.util.log.AbstractTreeLogger; import com.google.gwt.dev.util.log.PrintWriterTreeLogger; import junit.framework.TestCase; -import java.io.IOException; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; -public class TypeOracleBuilderTest extends TestCase { - private static abstract class TestCup implements CompilationUnitProvider { - private final String packageName; +public class TypeOracleMediatorTest extends TestCase { - private final String[] typeNames; - - /** - * Creates a new {@code TestCup} with several types. The first type in - * {@code typeNames} is considered to be the main type. - * - * @param packageName the package for the types in this {@code TestCup} - * @param typeNames the types for this {@code TestCup}. Must have at least - * one type. The first type is considered to be the main type for - * this {@code TestCup}. - */ - public TestCup(String packageName, String... typeNames) { - this.packageName = packageName; - this.typeNames = typeNames; - assert typeNames != null && typeNames.length > 0; - for (int i = 0; i < typeNames.length; i++) { - String typeName = typeNames[i]; - register(typeName, this); + private abstract class CheckedMockCompilationUnit extends MockCompilationUnit { + public CheckedMockCompilationUnit(String packageName, + String shortMainTypeName, String... shortTypeNames) { + super(Shared.makeTypeName(packageName, shortMainTypeName)); + register(getTypeName(), this); + for (String shortTypeName : shortTypeNames) { + register(Shared.makeTypeName(packageName, shortTypeName), this); } } + @Override + public abstract String getSource(); + public abstract void check(JClassType type) throws NotFoundException; - - public long getLastModified() throws UnableToCompleteException { - return 0; - } - - public String getLocation() { - return "transient source for " + this.packageName + "." - + this.typeNames[0]; - } - - public String getMainTypeName() { - return typeNames[0]; - } - - public String getPackageName() { - return packageName; - } - - public String[] getTypeNames() { - return typeNames; - } - - public boolean isTransient() { - return true; - } - } - - private static Map<String, TestCup> publicTypeNameToTestCupMap = new HashMap<String, TestCup>(); - - /** - * Creates a non-transient {@link CompilationUnitProvider} and - * adds it the {@link TypeOracleBuilder}. - * - * @throws UnableToCompleteException - */ - private static void addNonTransientCompilationUnitProvider( - TypeOracleBuilder builder, String qualifiedTypeName, CharSequence code) - throws UnableToCompleteException { - - final CompilationUnitProvider cup = TypeOracleTestingUtils.createCup( - qualifiedTypeName, code); - - CompilationUnitProvider nonTransientCup = new CompilationUnitProvider() { - public long getLastModified() throws UnableToCompleteException { - return cup.getLastModified(); - } - - public String getLocation() { - /* - * Fake out TypeOracleBuilder so it does not look for a file at this - * location. - */ - return "http://" + cup.getLocation(); - } - - public String getMainTypeName() { - return cup.getMainTypeName(); - } - - public String getPackageName() { - return cup.getPackageName(); - } - - public char[] getSource() throws UnableToCompleteException { - return cup.getSource(); - } - - public boolean isTransient() { - return false; - } - }; - - builder.addCompilationUnit(nonTransientCup); } private static void assertEqualArraysUnordered(Object[] expected, @@ -152,34 +71,52 @@ } } - private static void check(JClassType classInfo) throws NotFoundException { - final String qName = classInfo.getQualifiedSourceName(); - TestCup cup = publicTypeNameToTestCupMap.get(qName); - assertNotNull(cup); // should've been declared during TestCup ctor - cup.check(classInfo); + private static void assertIsAssignable(JClassType from, JClassType to) { + assertTrue("'" + from + "' should be assignable to '" + to + "'", + from.isAssignableTo(to)); + assertTrue("'" + to + "' should be assignable from '" + from + "'", + to.isAssignableFrom(from)); } - private static void register(String simpleTypeName, TestCup cup) { - String qName = cup.getPackageName() + "." + simpleTypeName; - publicTypeNameToTestCupMap.put(qName, cup); + private static void assertIsNotAssignable(JClassType from, JClassType to) { + assertFalse(from.isAssignableTo(to)); + assertFalse(to.isAssignableFrom(from)); } - protected TestCup CU_AfterAssimilate = new TestCup("test.assim", - "AfterAssimilate") { + private static void recordAssignability( + Map<JClassType, Set<JClassType>> assignabilityMap, JClassType from, + JClassType to) { + Set<JClassType> set = assignabilityMap.get(from); + if (set == null) { + set = new HashSet<JClassType>(); + assignabilityMap.put(from, set); + } + set.add(to); + } + + /** + * Public so that this will be initialized before the CUs. + */ + public final Map<String, CheckedMockCompilationUnit> publicTypeNameToTestCupMap = new HashMap<String, CheckedMockCompilationUnit>(); + + protected CheckedMockCompilationUnit CU_AfterAssimilate = new CheckedMockCompilationUnit( + "test.assim", "AfterAssimilate") { public void check(JClassType type) { - // Don't need to check the type itself. + assertEquals("test.assim.BeforeAssimilate", + type.getSuperclass().getQualifiedSourceName()); } - public char[] getSource() { + public String getSource() { StringBuffer sb = new StringBuffer(); sb.append("package test.assim;\n"); sb.append("class AfterAssimilate extends BeforeAssimilate { }"); - return sb.toString().toCharArray(); + return sb.toString(); } }; - protected TestCup CU_Assignable = new TestCup("test.sub", "Derived", - "BaseInterface", "DerivedInterface", "Derived.Nested") { + protected CheckedMockCompilationUnit CU_Assignable = new CheckedMockCompilationUnit( + "test.sub", "Derived", "BaseInterface", "DerivedInterface", + "Derived.Nested") { public void check(JClassType type) { if ("Derived".equals(type.getSimpleSourceName())) checkDerived(type); @@ -187,7 +124,7 @@ checkNested(type); } - public char[] getSource() { + public String getSource() { StringBuffer sb = new StringBuffer(); sb.append("package test.sub;\n"); sb.append("import test.Outer;"); @@ -196,7 +133,7 @@ sb.append("public class Derived extends Outer.Inner {\n"); sb.append(" public static class Nested extends Outer.Inner implements DerivedInterface { }\n"); sb.append("}\n"); - return sb.toString().toCharArray(); + return sb.toString(); } private void checkDerived(JClassType type) { @@ -209,22 +146,23 @@ } }; - protected TestCup CU_BeforeAssimilate = new TestCup("test.assim", - "BeforeAssimilate") { + protected CheckedMockCompilationUnit CU_BeforeAssimilate = new CheckedMockCompilationUnit( + "test.assim", "BeforeAssimilate") { public void check(JClassType type) { - // Don't need to check the type itself. + assertEquals("test.assim.BeforeAssimilate", type.getQualifiedSourceName()); } - public char[] getSource() { + public String getSource() { StringBuffer sb = new StringBuffer(); sb.append("package test.assim;\n"); sb.append("class BeforeAssimilate { }"); - return sb.toString().toCharArray(); + return sb.toString(); } }; - protected TestCup CU_BindToTypeScope = new TestCup("test", "BindToTypeScope", - "BindToTypeScope.Object", "BindToTypeScope.DerivedObject") { + protected CheckedMockCompilationUnit CU_BindToTypeScope = new CheckedMockCompilationUnit( + "test", "BindToTypeScope", "BindToTypeScope.Object", + "BindToTypeScope.DerivedObject") { public void check(JClassType type) throws NotFoundException { if ("BindToTypeScope".equals(type.getSimpleSourceName())) @@ -249,14 +187,14 @@ assertEquals("test.BindToTypeScope.Object", type.getQualifiedSourceName()); } - public char[] getSource() { + public String getSource() { StringBuffer sb = new StringBuffer(); sb.append("package test;\n"); sb.append("public class BindToTypeScope {\n"); sb.append(" public static class Object { }\n"); sb.append(" public static class DerivedObject extends Object { }\n"); sb.append("}\n"); - return sb.toString().toCharArray(); + return sb.toString(); } private void checkDerivedObject(JClassType type) throws NotFoundException { @@ -273,23 +211,25 @@ } }; - protected TestCup CU_DeclaresInnerGenericType = new TestCup( - "parameterized.type.build.dependency", "Class1") { + protected CheckedMockCompilationUnit CU_DeclaresInnerGenericType = new CheckedMockCompilationUnit( + "parameterized.type.build.dependency", "Class1", "Class1.Inner") { @Override public void check(JClassType type) throws NotFoundException { + assertNotNull(type.isGenericType()); } - public char[] getSource() throws UnableToCompleteException { + public String getSource() { StringBuilder sb = new StringBuilder(); sb.append("package parameterized.type.build.dependency;\n"); sb.append("public class Class1<T> {\n"); sb.append(" public interface Inner<T> {}\n"); sb.append("}\n"); - return sb.toString().toCharArray(); + return sb.toString(); } }; - protected TestCup CU_DefaultClass = new TestCup("test", "DefaultClass") { + protected CheckedMockCompilationUnit CU_DefaultClass = new CheckedMockCompilationUnit( + "test", "DefaultClass") { public void check(JClassType type) { assertEquals("DefaultClass", type.getSimpleSourceName()); assertEquals("test.DefaultClass", type.getQualifiedSourceName()); @@ -301,45 +241,47 @@ assertEquals(0, type.getFields().length); } - public char[] getSource() { + public String getSource() { StringBuffer sb = new StringBuffer(); sb.append("package test;\n"); sb.append("public class DefaultClass extends Object { }\n"); - return sb.toString().toCharArray(); + return sb.toString(); } }; - protected TestCup CU_ExtendsGenericList = new TestCup("test.refresh", - "ExtendsGenericList") { + protected CheckedMockCompilationUnit CU_ExtendsGenericList = new CheckedMockCompilationUnit( + "test.refresh", "ExtendsGenericList") { @Override public void check(JClassType type) throws NotFoundException { + assertNotNull(type.getSuperclass().isParameterized()); } - public char[] getSource() { + public String getSource() { StringBuilder sb = new StringBuilder(); sb.append("package test.refresh;\n"); sb.append("class ExtendsGenericList extends GenericList<Object> {}"); - return sb.toString().toCharArray(); + return sb.toString(); } }; - protected TestCup CU_ExtendsParameterizedType = new TestCup( + protected CheckedMockCompilationUnit CU_ExtendsParameterizedType = new CheckedMockCompilationUnit( "parameterized.type.build.dependency", "Class2") { @Override public void check(JClassType type) throws NotFoundException { + assertNotNull(type.getSuperclass().isParameterized()); } - public char[] getSource() throws UnableToCompleteException { + public String getSource() { StringBuilder sb = new StringBuilder(); sb.append("package parameterized.type.build.dependency;\n"); sb.append("public class Class2 extends Class1<Object> {}\n"); - return sb.toString().toCharArray(); + return sb.toString(); } }; - protected TestCup CU_FieldsAndTypes = new TestCup("test", "Fields", - "SomeType") { + protected CheckedMockCompilationUnit CU_FieldsAndTypes = new CheckedMockCompilationUnit( + "test", "Fields", "SomeType") { public void check(JClassType type) throws NotFoundException { if ("Fields".equals(type.getSimpleSourceName())) { assertEquals("test.Fields", type.getQualifiedSourceName()); @@ -423,12 +365,13 @@ assertEquals("int[][]", fieldType.getQualifiedSourceName()); } else { - // No need to check SomeType since there's already a DefaultClass + // No need to check SomeType since + // there's already a DefaultClass // test. } } - public char[] getSource() { + public String getSource() { StringBuffer sb = new StringBuffer(); sb.append("package test;\n"); sb.append("class SomeType { }"); @@ -446,56 +389,98 @@ sb.append(" private SomeType[] someTypeArray;\n"); sb.append(" private int[][] intArrayArray;\n"); sb.append("}\n"); - return sb.toString().toCharArray(); + return sb.toString(); } }; - protected TestCup CU_GenericList = new TestCup("test.refresh", "GenericList") { + protected CheckedMockCompilationUnit CU_GenericList = new CheckedMockCompilationUnit( + "test.refresh", "GenericList") { @Override public void check(JClassType type) throws NotFoundException { + assertNotNull(type.isGenericType()); } - public char[] getSource() throws UnableToCompleteException { + public String getSource() { StringBuilder sb = new StringBuilder(); sb.append("package test.refresh;\n"); sb.append("class GenericList<T> {\n"); sb.append(" public static final int CONSTANT = 0;\n"); sb.append("}"); - return sb.toString().toCharArray(); + return sb.toString(); } }; - protected TestCup CU_HasSyntaxErrors = new TestCup("test", "HasSyntaxErrors", - "NoSyntaxErrors") { + protected CheckedMockCompilationUnit CU_HasSyntaxErrors = new CheckedMockCompilationUnit( + "test", "HasSyntaxErrors", "NoSyntaxErrors") { public void check(JClassType classInfo) { fail("This class should have been removed"); } - public char[] getSource() { + public String getSource() { StringBuffer sb = new StringBuffer(); sb.append("package test;\n"); sb.append("class NoSyntaxErrors { }\n"); sb.append("public class HasSyntaxErrors { a syntax error }\n"); - return sb.toString().toCharArray(); + return sb.toString(); } }; - protected TestCup CU_HasUnresolvedSymbols = new TestCup("test", "Invalid", - "Valid") { + protected CheckedMockCompilationUnit CU_HasUnresolvedSymbols = new CheckedMockCompilationUnit( + "test", "Invalid", "Valid") { public void check(JClassType classInfo) { fail("Both classes should have been removed"); } - public char[] getSource() { + public String getSource() { StringBuffer sb = new StringBuffer(); sb.append("package test;\n"); sb.append("public class Invalid extends NoSuchClass { }\n"); sb.append("class Valid extends Object { }\n"); - return sb.toString().toCharArray(); + return sb.toString(); } }; - protected TestCup CU_MetaData = new TestCup("test", "MetaData") { + protected CheckedMockCompilationUnit CU_LocalClass = new CheckedMockCompilationUnit( + "test", "Enclosing", "Enclosing.1") { + + public void check(JClassType type) { + final String name = type.getSimpleSourceName(); + if ("Enclosing".equals(name)) + checkEnclosing(type); + else + checkLocal(type); + } + + public void checkEnclosing(JClassType type) { + assertEquals("Enclosing", type.getSimpleSourceName()); + assertEquals("test.Enclosing", type.getQualifiedSourceName()); + JClassType[] nested = type.getNestedTypes(); + assertEquals(1, nested.length); + JClassType inner = nested[0]; + assertEquals("test.Enclosing.1", inner.getQualifiedSourceName()); + } + + public void checkLocal(JClassType type) { + assertEquals("1", type.getSimpleSourceName()); + assertEquals("test.Enclosing.1", type.getQualifiedSourceName()); + assertEquals("test.Enclosing", + type.getEnclosingType().getQualifiedSourceName()); + } + + public String getSource() { + StringBuffer sb = new StringBuffer(); + sb.append("package test;\n"); + sb.append("public class Enclosing {\n"); + sb.append(" public static Object getLocal() {"); + sb.append(" return new Object() { };\n"); + sb.append(" }\n"); + sb.append("}\n"); + return sb.toString(); + } + }; + + protected CheckedMockCompilationUnit CU_MetaData = new CheckedMockCompilationUnit( + "test", "MetaData") { public void check(JClassType type) throws NotFoundException { { @@ -553,7 +538,7 @@ } } - public char[] getSource() { + public String getSource() { StringBuffer sb = new StringBuffer(); sb.append("package test;\n"); sb.append("/**\n"); @@ -574,11 +559,12 @@ sb.append(" private Object bar = null;\n"); sb.append(" private Object noMd = null;\n"); sb.append("}\n"); - return sb.toString().toCharArray(); + return sb.toString(); } }; - protected TestCup CU_MethodsAndParams = new TestCup("test", "Methods") { + protected CheckedMockCompilationUnit CU_MethodsAndParams = new CheckedMockCompilationUnit( + "test", "Methods") { public void check(JClassType type) throws NotFoundException { TypeOracle tio = type.getOracle(); @@ -634,7 +620,7 @@ assertEquals(0, thrownTypes.length); } - public char[] getSource() { + public String getSource() { StringBuffer sb = new StringBuffer(); sb.append("package test;\n"); sb.append("public class Methods {\n"); @@ -645,25 +631,27 @@ sb.append(" public void overloaded(int x, Object y) throws Throwable { return; }\n"); sb.append(" public Object overloaded(int x, char y) { return null; }\n"); sb.append("}\n"); - return sb.toString().toCharArray(); + return sb.toString(); } }; - protected TestCup CU_Object = new TestCup("java.lang", "Object") { + protected CheckedMockCompilationUnit CU_Object = new CheckedMockCompilationUnit( + "java.lang", "Object") { public void check(JClassType type) { assertEquals("Object", type.getSimpleSourceName()); assertEquals("java.lang.Object", type.getQualifiedSourceName()); } - public char[] getSource() { + public String getSource() { StringBuffer sb = new StringBuffer(); sb.append("package java.lang;"); sb.append("public class Object { }"); - return sb.toString().toCharArray(); + return sb.toString(); } }; - protected TestCup CU_OuterInner = new TestCup("test", "Outer", "Outer.Inner") { + protected CheckedMockCompilationUnit CU_OuterInner = new CheckedMockCompilationUnit( + "test", "Outer", "Outer.Inner") { public void check(JClassType type) { final String name = type.getSimpleSourceName(); @@ -689,75 +677,87 @@ assertEquals("test.Outer.Inner", inner.getQualifiedSourceName()); } - public char[] getSource() { + public String getSource() { StringBuffer sb = new StringBuffer(); sb.append("package test;\n"); sb.append("public class Outer {\n"); sb.append(" public static class Inner { }\n"); sb.append("}\n"); - return sb.toString().toCharArray(); + return sb.toString(); } }; - protected TestCup CU_ReferencesGenericListConstant = new TestCup( + protected CheckedMockCompilationUnit CU_ReferencesGenericListConstant = new CheckedMockCompilationUnit( "test.refresh", "ReferencesGenericListConstant") { @Override public void check(JClassType type) throws NotFoundException { + assertEquals("test.refresh.ReferencesGenericListConstant", + type.getQualifiedSourceName()); } - public char[] getSource() throws UnableToCompleteException { + public String getSource() { StringBuilder sb = new StringBuilder(); sb.append("package test.refresh;\n"); sb.append("class ReferencesGenericListConstant {\n"); sb.append(" public static final int MY_CONSTANT = GenericList.CONSTANT;\n"); sb.append("}"); - return sb.toString().toCharArray(); + return sb.toString(); } }; - protected TestCup CU_ReferencesParameterizedTypeBeforeItsGenericFormHasBeenProcessed = new TestCup( + protected CheckedMockCompilationUnit CU_ReferencesParameterizedTypeBeforeItsGenericFormHasBeenProcessed = new CheckedMockCompilationUnit( "parameterized.type.build.dependency", "Class0") { @Override public void check(JClassType type) throws NotFoundException { + JClassType[] intfs = type.getImplementedInterfaces(); + assertEquals(1, intfs.length); + assertNotNull(intfs[0].isParameterized()); } - public char[] getSource() throws UnableToCompleteException { + public String getSource() { StringBuilder sb = new StringBuilder(); sb.append("package parameterized.type.build.dependency;\n"); sb.append("public class Class0 implements Class2.Inner<Object> {\n"); sb.append("}\n"); - return sb.toString().toCharArray(); + return sb.toString(); } }; - protected TestCup CU_RefsInfectedCompilationUnit = new TestCup("test", - "RefsInfectedCompilationUnit") { + protected CheckedMockCompilationUnit CU_RefsInfectedCompilationUnit = new CheckedMockCompilationUnit( + "test", "RefsInfectedCompilationUnit") { public void check(JClassType classInfo) { fail("This class should should have been removed because it refers to a class in another compilation unit that had problems"); } - public char[] getSource() { + public String getSource() { StringBuffer sb = new StringBuffer(); sb.append("package test;\n"); sb.append("public class RefsInfectedCompilationUnit extends Valid { }\n"); - return sb.toString().toCharArray(); + return sb.toString(); } }; - protected TestCup CU_Throwable = new TestCup("java.lang", "Throwable") { + protected CheckedMockCompilationUnit CU_Throwable = new CheckedMockCompilationUnit( + "java.lang", "Throwable") { public void check(JClassType type) { assertEquals("Throwable", type.getSimpleSourceName()); assertEquals("java.lang.Throwable", type.getQualifiedSourceName()); } - public char[] getSource() { + public String getSource() { StringBuffer sb = new StringBuffer(); sb.append("package java.lang;"); sb.append("public class Throwable { }"); - return sb.toString().toCharArray(); + return sb.toString(); } }; + private final TypeOracleMediator mediator = new TypeOracleMediator(); + + private final TypeOracle typeOracle = mediator.getTypeOracle(); + + private final Set<CompilationUnit> units = new HashSet<CompilationUnit>(); + public void checkTypes(JClassType[] types) throws NotFoundException { for (int i = 0; i < types.length; i++) { JClassType type = types[i]; @@ -768,100 +768,123 @@ } } - public void testAssimilation() throws UnableToCompleteException { - TypeOracle typeOracle0 = new TypeOracle(); - TreeLogger logger = createTreeLogger(); + public void testAssignable() throws UnableToCompleteException, + TypeOracleException { + units.add(CU_Object); + units.add(CU_Assignable); + units.add(CU_OuterInner); + compileAndRefresh(); + JClassType[] allTypes = typeOracle.getTypes(); + assertEquals(7, allTypes.length); - // Build onto an empty type oracle. - // - TypeOracleBuilder builder1 = new TypeOracleBuilder(typeOracle0); - builder1.addCompilationUnit(CU_Object); - builder1.addCompilationUnit(CU_BeforeAssimilate); - TypeOracle typeOracle1 = builder1.build(logger); - assertSame(typeOracle0, typeOracle1); - assertEquals(2, typeOracle1.getTypes().length); - JClassType before = typeOracle1.findType("test.assim.BeforeAssimilate"); + Map<JClassType, Set<JClassType>> assignabilityMap = new HashMap<JClassType, Set<JClassType>>(); + + JClassType inner = typeOracle.findType("test.Outer.Inner"); + JClassType baseIntf = typeOracle.findType("test.sub.BaseInterface"); + JClassType derivedIntf = typeOracle.findType("test.sub.DerivedInterface"); + recordAssignability(assignabilityMap, derivedIntf, baseIntf); + JClassType derived = typeOracle.findType("test.sub.Derived"); + recordAssignability(assignabilityMap, derived, inner); + JClassType nested = typeOracle.findType("test.sub.Derived.Nested"); + recordAssignability(assignabilityMap, nested, inner); + recordAssignability(assignabilityMap, nested, derivedIntf); + recordAssignability(assignabilityMap, nested, baseIntf); + + for (JClassType fromType : allTypes) { + for (JClassType toType : allTypes) { + if (fromType == toType || toType == typeOracle.getJavaLangObject()) { + assertIsAssignable(fromType, toType); + } else { + Set<JClassType> set = assignabilityMap.get(fromType); + if (set != null && set.contains(toType)) { + assertIsAssignable(fromType, toType); + } else { + assertIsNotAssignable(fromType, toType); + } + } + } + } + } + + public void testAssimilation() throws UnableToCompleteException, + TypeOracleException { + units.add(CU_Object); + units.add(CU_BeforeAssimilate); + compileAndRefresh(); + assertEquals(2, typeOracle.getTypes().length); + JClassType before = typeOracle.findType("test.assim.BeforeAssimilate"); // Build onto an existing type oracle. - // - TypeOracleBuilder builder2 = new TypeOracleBuilder(typeOracle1); - builder2.addCompilationUnit(CU_AfterAssimilate); - TypeOracle typeOracle2 = builder2.build(logger); - assertSame(typeOracle1, typeOracle2); - assertEquals(3, typeOracle2.getTypes().length); + units.add(CU_AfterAssimilate); + compileAndRefresh(); + assertEquals(3, typeOracle.getTypes().length); // Make sure identities remained intact across the assimilation. - // - JClassType after = typeOracle2.findType("test.assim.AfterAssimilate"); - + JClassType after = typeOracle.findType("test.assim.AfterAssimilate"); assertSame(before, after.getSuperclass()); } public void testBindToTypeScope() throws TypeOracleException, UnableToCompleteException { - TypeOracleBuilder tiob = createTypeInfoOracleBuilder(); - tiob.addCompilationUnit(CU_Object); - tiob.addCompilationUnit(CU_BindToTypeScope); - TypeOracle tio = tiob.build(createTreeLogger()); - JClassType[] types = tio.getTypes(); + units.add(CU_Object); + units.add(CU_BindToTypeScope); + compileAndRefresh(); + JClassType[] types = typeOracle.getTypes(); assertEquals(4, types.length); - checkTypes(types); } public void testDefaultClass() throws TypeOracleException, UnableToCompleteException { - TypeOracleBuilder tiob = createTypeInfoOracleBuilder(); - tiob.addCompilationUnit(CU_Object); - tiob.addCompilationUnit(CU_DefaultClass); - TypeOracle tio = tiob.build(createTreeLogger()); - JClassType[] types = tio.getTypes(); + units.add(CU_Object); + units.add(CU_DefaultClass); + compileAndRefresh(); + JClassType[] types = typeOracle.getTypes(); assertEquals(2, types.length); - checkTypes(types); } public void testFieldsAndTypes() throws TypeOracleException, UnableToCompleteException { - TypeOracleBuilder tiob = createTypeInfoOracleBuilder(); - tiob.addCompilationUnit(CU_Object); - tiob.addCompilationUnit(CU_FieldsAndTypes); - TypeOracle tio = tiob.build(createTreeLogger()); - JClassType[] types = tio.getTypes(); + units.add(CU_Object); + units.add(CU_FieldsAndTypes); + compileAndRefresh(); + JClassType[] types = typeOracle.getTypes(); assertEquals(3, types.length); - checkTypes(types); + } + + public void testLocal() throws TypeOracleException, UnableToCompleteException { + units.add(CU_Object); + units.add(CU_LocalClass); + compileAndRefresh(); + JClassType[] types = typeOracle.getTypes(); + assertEquals(3, types.length); } public void testMetaData() throws TypeOracleException, UnableToCompleteException { - TypeOracleBuilder tiob = createTypeInfoOracleBuilder(); - tiob.addCompilationUnit(CU_Object); - tiob.addCompilationUnit(CU_MetaData); - TypeOracle tio = tiob.build(createTreeLogger()); - JClassType[] types = tio.getTypes(); + units.add(CU_Object); + units.add(CU_MetaData); + compileAndRefresh(); + JClassType[] types = typeOracle.getTypes(); assertEquals(2, types.length); - checkTypes(types); } public void testMethodsAndParams() throws TypeOracleException, UnableToCompleteException { - TypeOracleBuilder tiob = createTypeInfoOracleBuilder(); - tiob.addCompilationUnit(CU_Object); - tiob.addCompilationUnit(CU_Throwable); - tiob.addCompilationUnit(CU_MethodsAndParams); - TypeOracle tio = tiob.build(createTreeLogger()); - JClassType[] types = tio.getTypes(); + units.add(CU_Object); + units.add(CU_Throwable); + units.add(CU_MethodsAndParams); + compileAndRefresh(); + JClassType[] types = typeOracle.getTypes(); assertEquals(3, types.length); - checkTypes(types); } public void testOuterInner() throws TypeOracleException, UnableToCompleteException { - TypeOracleBuilder tiob = createTypeInfoOracleBuilder(); - tiob.addCompilationUnit(CU_Object); - tiob.addCompilationUnit(CU_OuterInner); - TypeOracle tio = tiob.build(createTreeLogger()); - JClassType[] types = tio.getTypes(); + units.add(CU_Object); + units.add(CU_OuterInner); + compileAndRefresh(); + JClassType[] types = typeOracle.getTypes(); assertEquals(3, types.length); - checkTypes(types); } /** @@ -873,16 +896,14 @@ * CU_DeclaresInnerGenericType. */ public void testParameterizedTypeBuildDependencies() - throws UnableToCompleteException { - TypeOracleBuilder tiob = createTypeInfoOracleBuilder(); + throws UnableToCompleteException, TypeOracleException { + units.add(CU_ReferencesParameterizedTypeBeforeItsGenericFormHasBeenProcessed); + units.add(CU_ExtendsParameterizedType); + units.add(CU_DeclaresInnerGenericType); + units.add(CU_Object); - tiob.addCompilationUnit(CU_ReferencesParameterizedTypeBeforeItsGenericFormHasBeenProcessed); - tiob.addCompilationUnit(CU_ExtendsParameterizedType); - tiob.addCompilationUnit(CU_DeclaresInnerGenericType); - tiob.addCompilationUnit(CU_Object); - - TypeOracle tio = tiob.build(createTreeLogger()); - assertNull(tio.findType("test.parameterizedtype.build.dependencies.Class2")); + compileAndRefresh(); + assertNull(typeOracle.findType("test.parameterizedtype.build.dependencies.Class2")); } /** @@ -893,44 +914,42 @@ * @throws NotFoundException * @throws IOException */ - public void testRefresh() throws UnableToCompleteException, NotFoundException { - TypeOracleBuilder tiob = createTypeInfoOracleBuilder(); + public void testRefresh() throws UnableToCompleteException, + TypeOracleException { + units.add(CU_Object); + units.add(CU_ExtendsGenericList); + units.add(CU_GenericList); + units.add(CU_ReferencesGenericListConstant); - tiob.addCompilationUnit(CU_Object); - tiob.addCompilationUnit(CU_ExtendsGenericList); - tiob.addCompilationUnit(CU_GenericList); - tiob.addCompilationUnit(CU_ReferencesGenericListConstant); - - TreeLogger logger = createTreeLogger(); - TypeOracle to = tiob.build(logger); + compileAndRefresh(); // Get the types produced by the TypeOracle - JClassType extendsGenericListType = to.getType("test.refresh.ExtendsGenericList"); - JClassType genericListType = to.getType("test.refresh.GenericList"); - JClassType objectType = to.getJavaLangObject(); - JClassType referencesGenericListConstantType = to.getType("test.refresh.ReferencesGenericListConstant"); + JClassType extendsGenericListType = typeOracle.getType("test.refresh.ExtendsGenericList"); + JClassType genericListType = typeOracle.getType("test.refresh.GenericList"); + JClassType objectType = typeOracle.getJavaLangObject(); + JClassType referencesGenericListConstantType = typeOracle.getType("test.refresh.ReferencesGenericListConstant"); /* - * Add the CU_GenericList again and simulate a refresh. This should cause + * Invalidate CU_GenericList and simulate a refresh. This should cause * anything that depends on GenericList to be rebuilt by the type oracle. */ - tiob.addCompilationUnit(CU_GenericList); - refreshTypeOracle(tiob, to); - to = tiob.build(logger); + CU_GenericList.setState(State.FRESH); + compileAndRefresh(); assertNotSame(genericListType.getQualifiedSourceName() + "; ", - to.getType("test.refresh.GenericList"), genericListType); + typeOracle.getType("test.refresh.GenericList"), genericListType); assertNotSame(extendsGenericListType.getQualifiedSourceName() + "; ", - to.getType("test.refresh.ExtendsGenericList"), extendsGenericListType); + typeOracle.getType("test.refresh.ExtendsGenericList"), + extendsGenericListType); assertSame(objectType.getQualifiedSourceName() + "; ", - to.getJavaLangObject(), objectType); + typeOracle.getJavaLangObject(), objectType); /* * Make sure that referencing a constant field will cause a type to be * rebuilt if the constant changes. */ assertNotSame(referencesGenericListConstantType.getQualifiedSourceName(), - to.getType("test.refresh.ReferencesGenericListConstant"), + typeOracle.getType("test.refresh.ReferencesGenericListConstant"), referencesGenericListConstantType); } @@ -946,21 +965,19 @@ * @throws UnableToCompleteException * @throws IOException */ - public void testRefreshWithErrors() throws UnableToCompleteException { - TypeOracleBuilder tiob = createTypeInfoOracleBuilder(); - + public void testRefreshWithErrors() throws UnableToCompleteException, + TypeOracleException { // Add Object StringBuffer sb = new StringBuffer(); sb.append("package java.lang;"); sb.append("public class Object { }"); - addNonTransientCompilationUnitProvider(tiob, "java.lang.Object", sb); + addCompilationUnit("java.lang.Object", sb); // Add UnmodifiedClass that will never change. sb = new StringBuffer(); sb.append("package test.refresh.with.errors;"); sb.append("public class UnmodifiedClass { }"); - addNonTransientCompilationUnitProvider(tiob, - "test.refresh.with.errors.UnmodifiedClass", sb); + addCompilationUnit("test.refresh.with.errors.UnmodifiedClass", sb); // Add GoodClass that references a class that will go bad. sb = new StringBuffer(); @@ -968,95 +985,113 @@ sb.append("public class GoodClass {\n"); sb.append(" ClassThatWillGoBad ctwgb;\n"); sb.append("}\n"); - addNonTransientCompilationUnitProvider(tiob, - "test.refresh.with.errors.GoodClass", sb); + addCompilationUnit("test.refresh.with.errors.GoodClass", sb); // Add ClassThatWillGoBad that goes bad on the next refresh. - StaticCompilationUnitProvider cupThatWillGoBad = new StaticCompilationUnitProvider( - "test.refresh.with.errors", "ClassThatWillGoBad", null) { - boolean goBad = false; + MockCompilationUnit unitThatWillGoBad = new MockCompilationUnit( + "test.refresh.with.errors.ClassThatWillGoBad") { + private String source = "package test.refresh.with.errors;\n" + + "public class ClassThatWillGoBad { }\n"; @Override - public char[] getSource() { - StringBuffer sb = new StringBuffer(); + public String getSource() { + return source; + } - if (!goBad) { - sb.append("package test.refresh.with.errors;\n"); - sb.append("public class ClassThatWillGoBad {\n"); - sb.append("}\n"); - goBad = true; - } else { - sb.append("This will cause a syntax error."); - } - return sb.toString().toCharArray(); + @Override + void setState(State newState) { + super.setState(newState); + source = "This will cause a syntax error."; } }; - tiob.addCompilationUnit(cupThatWillGoBad); + units.add(unitThatWillGoBad); - TreeLogger logger = createTreeLogger(); - TypeOracle to = tiob.build(logger); - assertNotNull(to.findType("test.refresh.with.errors.UnmodifiedClass")); - assertNotNull(to.findType("test.refresh.with.errors.GoodClass")); - assertNotNull(to.findType("test.refresh.with.errors.ClassThatWillGoBad")); + compileAndRefresh(); - // Add AnotherGoodClass that references a class that was not recompiled. + assertNotNull(typeOracle.findType("test.refresh.with.errors.UnmodifiedClass")); + assertNotNull(typeOracle.findType("test.refresh.with.errors.GoodClass")); + assertNotNull(typeOracle.findType("test.refresh.with.errors.ClassThatWillGoBad")); + + // Add AnotherGoodClass that references a + // class that was not recompiled. sb = new StringBuffer(); sb.append("package test.refresh.with.errors;\n"); sb.append("public class AnotherGoodClass {\n"); sb.append(" UnmodifiedClass uc; // This will cause the runaway pruning.\n"); sb.append("}\n"); - addNonTransientCompilationUnitProvider(tiob, - "test.refresh.with.errors.AnotherGoodClass", sb); + addCompilationUnit("test.refresh.with.errors.AnotherGoodClass", sb); - // Add BadClass that has errors and originally forced issue 2238. + // Add BadClass that has errors and originally + // forced issue 2238. sb = new StringBuffer(); sb.append("package test.refresh.with.errors;\n"); sb.append("public class BadClass {\n"); sb.append(" This will trigger a syntax error.\n"); sb.append("}\n"); - addNonTransientCompilationUnitProvider(tiob, - "test.refresh.with.errors.BadClass", sb); + addCompilationUnit("test.refresh.with.errors.BadClass", sb); // Now this cup should cause errors. - tiob.addCompilationUnit(cupThatWillGoBad); + unitThatWillGoBad.setState(State.FRESH); - refreshTypeOracle(tiob, to); - to = tiob.build(logger); + compileAndRefresh(); - assertNotNull(to.findType("test.refresh.with.errors.UnmodifiedClass")); - assertNotNull(to.findType("test.refresh.with.errors.AnotherGoodClass")); - assertNull(to.findType("test.refresh.with.errors.BadClass")); - assertNull(to.findType("test.refresh.with.errors.ClassThatWillGoBad")); - assertNull(to.findType("test.refresh.with.errors.GoodClass")); + assertNotNull(typeOracle.findType("test.refresh.with.errors.UnmodifiedClass")); + assertNotNull(typeOracle.findType("test.refresh.with.errors.AnotherGoodClass")); + assertNull(typeOracle.findType("test.refresh.with.errors.BadClass")); + assertNull(typeOracle.findType("test.refresh.with.errors.ClassThatWillGoBad")); + assertNull(typeOracle.findType("test.refresh.with.errors.GoodClass")); } public void testSyntaxErrors() throws TypeOracleException, UnableToCompleteException { - TypeOracleBuilder tiob = createTypeInfoOracleBuilder(); - tiob.addCompilationUnit(CU_Object); - tiob.addCompilationUnit(CU_HasSyntaxErrors); - TypeOracle tio = tiob.build(createTreeLogger()); - JClassType[] types = tio.getTypes(); + units.add(CU_Object); + units.add(CU_HasSyntaxErrors); + compileAndRefresh(); + JClassType[] types = typeOracle.getTypes(); // Only java.lang.Object should remain. // assertEquals(1, types.length); assertEquals("java.lang.Object", types[0].getQualifiedSourceName()); - checkTypes(types); } public void testUnresolvedSymbls() throws TypeOracleException, UnableToCompleteException { - TypeOracleBuilder tiob = createTypeInfoOracleBuilder(); - tiob.addCompilationUnit(CU_Object); - tiob.addCompilationUnit(CU_HasUnresolvedSymbols); - tiob.addCompilationUnit(CU_RefsInfectedCompilationUnit); - TypeOracle tio = tiob.build(createTreeLogger()); - JClassType[] types = tio.getTypes(); + units.add(CU_Object); + units.add(CU_HasUnresolvedSymbols); + units.add(CU_RefsInfectedCompilationUnit); + compileAndRefresh(); + JClassType[] types = typeOracle.getTypes(); // Only java.lang.Object should remain. // assertEquals(1, types.length); assertEquals("java.lang.Object", types[0].getQualifiedSourceName()); - checkTypes(types); + } + + /** + * Creates a {@link CompilationUnit} and adds it the set of units. + * + * @throws UnableToCompleteException + */ + private void addCompilationUnit(String qualifiedTypeName, CharSequence source) { + units.add(new MockCompilationUnit(qualifiedTypeName, source.toString())); + } + + private void check(JClassType classInfo) throws NotFoundException { + final String qName = classInfo.getQualifiedSourceName(); + CheckedMockCompilationUnit cup = publicTypeNameToTestCupMap.get(qName); + if (cup != null) { + cup.check(classInfo); + } + } + + private void compileAndRefresh() throws UnableToCompleteException, + TypeOracleException { + TreeLogger logger = createTreeLogger(); + CompilationUnitInvalidator.invalidateUnitsWithInvalidRefs(logger, units); + JdtCompiler.compile(units); + CompilationUnitInvalidator.invalidateUnitsWithErrors(logger, units); + mediator.refresh(logger, units); + checkTypes(typeOracle.getTypes()); } /** @@ -1073,12 +1108,7 @@ } } - private TypeOracleBuilder createTypeInfoOracleBuilder() { - return new TypeOracleBuilder(); - } - - private void refreshTypeOracle(TypeOracleBuilder tiob, TypeOracle to) { - CacheManager cacheManager = tiob.getCacheManager(); - cacheManager.invalidateOnRefresh(to); + private void register(String qualifiedTypeName, CheckedMockCompilationUnit cup) { + publicTypeNameToTestCupMap.put(qualifiedTypeName, cup); } }
diff --git a/dev/core/test/com/google/gwt/dev/javac/TypeOracleTestingUtils.java b/dev/core/test/com/google/gwt/dev/javac/TypeOracleTestingUtils.java new file mode 100644 index 0000000..4a9a2e5 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/javac/TypeOracleTestingUtils.java
@@ -0,0 +1,76 @@ +/* + * Copyright 2008 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.TreeLogger; +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.core.ext.typeinfo.TypeOracle; +import com.google.gwt.dev.javac.impl.SourceFileCompilationUnit; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * Utilities for tests that build a type oracle and watch for errors. + * + */ +public class TypeOracleTestingUtils { + + public static TypeOracle buildStandardTypeOracleWith(TreeLogger logger, + CompilationUnit... extraUnits) throws UnableToCompleteException { + Set<CompilationUnit> extraUnitSet = new HashSet<CompilationUnit>(); + Collections.addAll(extraUnitSet, extraUnits); + return buildStandardTypeOracleWith(logger, extraUnitSet); + } + + public static TypeOracle buildStandardTypeOracleWith(TreeLogger logger, + Set<CompilationUnit> extraUnits) throws UnableToCompleteException { + Set<CompilationUnit> unitSet = new HashSet<CompilationUnit>(); + addStandardCups(unitSet); + for (CompilationUnit extraUnit : extraUnits) { + unitSet.add(extraUnit); + } + return buildTypeOracle(logger, unitSet); + } + + /** + * Add compilation units for basic classes like Object and String. + */ + private static void addStandardCups(Set<CompilationUnit> units) { + for (JavaSourceFile resource : JavaSourceCodeBase.getStandardResources()) { + units.add(new SourceFileCompilationUnit(resource)); + } + } + + private static TypeOracle buildTypeOracle(TreeLogger logger, + Set<CompilationUnit> units) throws UnableToCompleteException { + JdtCompiler.compile(units); + Map<String, CompiledClass> classMap = new HashMap<String, CompiledClass>(); + for (CompilationUnit unit : units) { + for (CompiledClass compiledClass : unit.getCompiledClasses()) { + classMap.put(compiledClass.getBinaryName(), compiledClass); + } + } + CompilationUnitInvalidator.validateCompilationUnits(units, classMap); + CompilationUnitInvalidator.invalidateUnitsWithErrors(logger, units); + TypeOracleMediator mediator = new TypeOracleMediator(); + mediator.refresh(logger, units); + return mediator.getTypeOracle(); + } +}
diff --git a/dev/core/test/com/google/gwt/dev/javac/impl/JavaResourceBase.java b/dev/core/test/com/google/gwt/dev/javac/impl/JavaResourceBase.java new file mode 100644 index 0000000..43f6657 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/javac/impl/JavaResourceBase.java
@@ -0,0 +1,141 @@ +/* + * Copyright 2008 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.impl; + +/** + * Contains standard Java source files for testing. + */ +public class JavaResourceBase { + + public static final MockResource ANNOTATION = new MockJavaResource( + "java.lang.annotation.Annotation") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package java.lang.annotation;\n"); + code.append("public interface Annotation {\n"); + code.append("}\n"); + return code; + } + }; + public static final MockJavaResource BAR = new MockJavaResource("test.Bar") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package test;\n"); + code.append("public class Bar extends Foo {\n"); + code.append(" public String value() { return \"Bar\"; }\n"); + code.append("}\n"); + return code; + } + }; + public static final MockResource CLASS = new MockJavaResource( + "java.lang.Class") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package java.lang;\n"); + code.append("public class Class<T> {\n"); + code.append("}\n"); + return code; + } + }; + public static final MockJavaResource FOO = new MockJavaResource("test.Foo") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package test;\n"); + code.append("public class Foo {\n"); + code.append(" public String value() { return \"Foo\"; }\n"); + code.append("}\n"); + return code; + } + }; + public static final MockResource JAVASCRIPTOBJECT = new MockJavaResource( + "com.google.gwt.core.client.JavaScriptObject") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package com.google.gwt.core.client;\n"); + code.append("public class JavaScriptObject {\n"); + code.append(" protected JavaScriptObject() { }\n"); + code.append("}\n"); + return code; + } + }; + public static final MockResource MAP = new MockJavaResource("java.util.Map") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package java.util;\n"); + code.append("public interface Map<K,V> { }\n"); + return code; + } + }; + public static final MockResource OBJECT = new MockJavaResource( + "java.lang.Object") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package java.lang;\n"); + code.append("public class Object {\n"); + code.append(" public String toString() { return \"Object\"; }\n"); + code.append(" public Object clone() { return this; } "); + code.append("}\n"); + return code; + } + }; + public static final MockResource SERIALIZABLE = new MockJavaResource( + "java.lang.Serializable") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package java.lang;\n"); + code.append("public interface Serializable { }\n"); + return code; + } + }; + public static final MockResource STRING = new MockJavaResource( + "java.lang.String") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package java.lang;\n"); + code.append("public final class String {\n"); + code.append(" public int length() { return 0; }\n"); + code.append("}\n"); + return code; + } + }; + public static final MockResource SUPPRESS_WARNINGS = new MockJavaResource( + "java.lang.SuppressWarnings") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package java.lang;\n"); + code.append("public @interface SuppressWarnings {\n"); + code.append(" String[] value();\n"); + code.append("}\n"); + return code; + } + }; + + public static MockResource[] getStandardResources() { + return new MockResource[] { + ANNOTATION, CLASS, JAVASCRIPTOBJECT, MAP, OBJECT, SERIALIZABLE, STRING, + SUPPRESS_WARNINGS}; + } +}
diff --git a/dev/core/test/com/google/gwt/dev/javac/impl/JavaSourceOracleImplTest.java b/dev/core/test/com/google/gwt/dev/javac/impl/JavaSourceOracleImplTest.java new file mode 100644 index 0000000..b063893 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/javac/impl/JavaSourceOracleImplTest.java
@@ -0,0 +1,145 @@ +/* + * Copyright 2008 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.impl; + +import com.google.gwt.dev.javac.JavaSourceFile; +import com.google.gwt.dev.resource.Resource; + +import junit.framework.TestCase; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +/** + * Tests {@link JavaSourceOracleImpl} using a mock {@link ResourceOracle}. + */ +public class JavaSourceOracleImplTest extends TestCase { + + private MockResourceOracle resourceOracle = new MockResourceOracle( + JavaResourceBase.getStandardResources()); + + private JavaSourceOracleImpl sourceOracle = new JavaSourceOracleImpl( + resourceOracle); + + public void testAdd() { + validateSourceOracle(); + + Map<String, JavaSourceFile> originalSourceMap = sourceOracle.getSourceMap(); + resourceOracle.add(JavaResourceBase.FOO); + Map<String, JavaSourceFile> newSourceMap = sourceOracle.getSourceMap(); + assertNotSame(originalSourceMap, newSourceMap); + assertEquals(originalSourceMap.size() + 1, newSourceMap.size()); + validateSourceOracle(); + } + + public void testBasic() { + validateSourceOracle(); + } + + public void testEmpty() { + resourceOracle = new MockResourceOracle(); + sourceOracle = new JavaSourceOracleImpl(resourceOracle); + validateSourceOracle(); + } + + public void testRemove() { + validateSourceOracle(); + + Map<String, JavaSourceFile> originalSourceMap = sourceOracle.getSourceMap(); + resourceOracle.remove(JavaResourceBase.OBJECT.getPath()); + Map<String, JavaSourceFile> newSourceMap = sourceOracle.getSourceMap(); + assertNotSame(originalSourceMap, newSourceMap); + assertEquals(originalSourceMap.size() - 1, newSourceMap.size()); + validateSourceOracle(); + } + + public void testReplace() { + validateSourceOracle(); + + Map<String, JavaSourceFile> originalSourceMap = sourceOracle.getSourceMap(); + resourceOracle.replace(new MockResource(JavaResourceBase.OBJECT.getPath()) { + @Override + protected CharSequence getContent() { + return JavaResourceBase.OBJECT.getContent(); + } + }); + Map<String, JavaSourceFile> newSourceMap = sourceOracle.getSourceMap(); + assertNotSame(originalSourceMap, newSourceMap); + assertEquals(originalSourceMap.size(), newSourceMap.size()); + assertFalse(originalSourceMap.equals(newSourceMap)); + validateSourceOracle(); + } + + public void testReplaceWithSame() { + validateSourceOracle(); + + Map<String, JavaSourceFile> originalSourceMap = sourceOracle.getSourceMap(); + resourceOracle.replace(JavaResourceBase.OBJECT); + Map<String, JavaSourceFile> newSourceMap = sourceOracle.getSourceMap(); + assertNotSame(originalSourceMap, newSourceMap); + assertEquals(originalSourceMap.size(), newSourceMap.size()); + assertEquals(originalSourceMap, newSourceMap); + validateSourceOracle(); + } + + /** + * Validate that the source oracle accurately reflects the resource oracle. + */ + private void validateSourceOracle() { + // Save off the reflected collections. + Map<String, JavaSourceFile> sourceMap = sourceOracle.getSourceMap(); + Set<String> classNames = sourceOracle.getClassNames(); + Set<JavaSourceFile> sourceFiles = sourceOracle.getSourceFiles(); + + // Validate that the collections are consistent with each other. + assertEquals(sourceMap.keySet(), classNames); + assertEquals(new HashSet<JavaSourceFile>(sourceMap.values()), sourceFiles); + + // Save off a mutable copy of the resource map to compare with. + Map<String, Resource> resourceMap = new HashMap<String, Resource>( + resourceOracle.getResourceMap()); + assertEquals(resourceMap.size(), sourceMap.size()); + for (Entry<String, JavaSourceFile> entry : sourceMap.entrySet()) { + // Validate source file internally consistent. + String className = entry.getKey(); + JavaSourceFile sourceFile = entry.getValue(); + assertEquals(className, sourceFile.getTypeName()); + assertEquals(Shared.getPackageName(className), + sourceFile.getPackageName()); + assertEquals(Shared.getShortName(className), sourceFile.getShortName()); + + // Find the matching resource (and remove it from the resource map!) + String expectedPath = Shared.toPath(className); + assertTrue(resourceMap.containsKey(expectedPath)); + + // Validate the source file matches the resource. + Resource resource = resourceMap.remove(expectedPath); + assertNotNull(resource); + assertEquals(Shared.readContent(resource.openContents()), + sourceFile.readSource()); + } + // The resource map should be empty now. + assertEquals(0, resourceMap.size()); + + // Validate collection identity hasn't changed. + assertSame(sourceMap, sourceOracle.getSourceMap()); + assertSame(sourceFiles, sourceOracle.getSourceFiles()); + assertSame(classNames, sourceOracle.getClassNames()); + } +}
diff --git a/dev/core/test/com/google/gwt/dev/javac/impl/JdtBehaviorTest.java b/dev/core/test/com/google/gwt/dev/javac/impl/JdtBehaviorTest.java new file mode 100644 index 0000000..fb4115a --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/javac/impl/JdtBehaviorTest.java
@@ -0,0 +1,245 @@ +/* + * Copyright 2008 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.impl; + +import junit.framework.TestCase; + +import org.eclipse.jdt.core.compiler.CategorizedProblem; +import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.internal.compiler.ClassFile; +import org.eclipse.jdt.internal.compiler.CompilationResult; +import org.eclipse.jdt.internal.compiler.Compiler; +import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies; +import org.eclipse.jdt.internal.compiler.ICompilerRequestor; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; +import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; +import org.eclipse.jdt.internal.compiler.env.INameEnvironment; +import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; +import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; +import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +/** + * Validates certain JDT behaviors that the compilation process may depend on + * for correctness. One useful aspect would be that if we upgrade JDT in the + * future, this test could help validate our assumptions. + */ +public class JdtBehaviorTest extends TestCase { + + /** + * Hook-point if we need to modify the compiler behavior. + */ + private class CompilerImpl extends Compiler { + + public CompilerImpl(INameEnvironment environment, + ICompilerRequestor requestor) { + super(environment, DefaultErrorHandlingPolicies.proceedWithAllProblems(), + getCompilerOptions(), requestor, new DefaultProblemFactory( + Locale.getDefault())); + } + } + + /** + * Hook point to accept results. + */ + private class ICompilerRequestorImpl implements ICompilerRequestor { + public void acceptResult(CompilationResult result) { + if (result.hasErrors()) { + StringBuilder sb = new StringBuilder(); + for (CategorizedProblem problem : result.getErrors()) { + sb.append(problem.toString()); + sb.append('\n'); + } + fail(sb.toString()); + } + for (ClassFile classFile : result.getClassFiles()) { + char[][] classNameArray = classFile.getCompoundName(); + char[][] packageArray = CharOperation.subarray(classNameArray, 0, + classNameArray.length - 1); + char[] packageName = CharOperation.concatWith(packageArray, '.'); + char[] className = CharOperation.concatWith(classNameArray, '.'); + addPackages(String.valueOf(packageName)); + classFiles.put(String.valueOf(className), classFile); + } + } + + private void addPackages(String packageName) { + while (true) { + packages.add(String.valueOf(packageName)); + int pos = packageName.lastIndexOf('.'); + if (pos > 0) { + packageName = packageName.substring(0, pos); + } else { + packages.add(""); + break; + } + } + } + } + + /** + * How JDT receives files from the environment. + */ + private class INameEnvironmentImpl implements INameEnvironment { + public void cleanup() { + // intentionally blank + } + + public NameEnvironmentAnswer findType(char[] type, char[][] pkg) { + return findType(CharOperation.arrayConcat(pkg, type)); + } + + public NameEnvironmentAnswer findType(char[][] compoundTypeName) { + final char[] typeChars = CharOperation.concatWith(compoundTypeName, '.'); + String typeName = String.valueOf(typeChars); + // System.out.println("findType: " + typeName); + ClassFile classFile = classFiles.get(typeName); + if (classFile != null) { + try { + byte[] bytes = classFile.getBytes(); + char[] loc = classFile.fileName(); + ClassFileReader cfr = new ClassFileReader(bytes, loc); + return new NameEnvironmentAnswer(cfr, null); + } catch (ClassFormatException e) { + throw new RuntimeException("Unexpectedly unable to parse class file", + e); + } + } + return null; + } + + public boolean isPackage(char[][] parentPkg, char[] pkg) { + final char[] pathChars = CharOperation.concatWith(parentPkg, pkg, '.'); + String packageName = String.valueOf(pathChars); + // System.out.println("isPackage: " + packageName); + return packages.contains(packageName); + } + } + + private class ResourceAdapter implements ICompilationUnit { + + private final MockJavaSourceFile sourceFile; + + public ResourceAdapter(MockResource resource) { + sourceFile = new MockJavaSourceFile(resource); + } + + public char[] getContents() { + return sourceFile.readSource().toCharArray(); + } + + public char[] getFileName() { + return sourceFile.getLocation().toCharArray(); + } + + public char[] getMainTypeName() { + return sourceFile.getShortName().toCharArray(); + } + + public char[][] getPackageName() { + return CharOperation.splitOn('.', + sourceFile.getPackageName().toCharArray()); + } + + @Override + public String toString() { + return sourceFile.toString(); + } + } + + private static CompilerOptions getCompilerOptions() { + Map<String, String> settings = new HashMap<String, String>(); + settings.put(CompilerOptions.OPTION_LineNumberAttribute, + CompilerOptions.GENERATE); + settings.put(CompilerOptions.OPTION_SourceFileAttribute, + CompilerOptions.GENERATE); + /* + * Tricks like "boolean stopHere = true;" depend on this setting to work in + * hosted mode. In web mode, our compiler should optimize them out once we + * do real data flow. + */ + settings.put(CompilerOptions.OPTION_PreserveUnusedLocal, + CompilerOptions.PRESERVE); + settings.put(CompilerOptions.OPTION_ReportDeprecation, + CompilerOptions.IGNORE); + settings.put(CompilerOptions.OPTION_LocalVariableAttribute, + CompilerOptions.GENERATE); + settings.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_1_5); + settings.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_1_5); + settings.put(CompilerOptions.OPTION_TargetPlatform, + CompilerOptions.VERSION_1_5); + + // This is needed by TypeOracleBuilder to parse metadata. + settings.put(CompilerOptions.OPTION_DocCommentSupport, + CompilerOptions.ENABLED); + return new CompilerOptions(settings); + } + + protected Map<String, ClassFile> classFiles = new HashMap<String, ClassFile>(); + + /** + * New object for each test case. + */ + protected INameEnvironmentImpl environment = new INameEnvironmentImpl(); + + protected Set<String> packages = new HashSet<String>(); + + /** + * New object for each test case. + */ + protected ICompilerRequestorImpl requestor = new ICompilerRequestorImpl(); + + /** + * New object for each test case. + */ + CompilerImpl compiler = new CompilerImpl(environment, requestor); + + public void testIncrementalBuild() { + List<MockResource> resources = new ArrayList<MockResource>(); + Collections.addAll(resources, JavaResourceBase.getStandardResources()); + resources.add(JavaResourceBase.FOO); + doCompile(resources); + + // Now incremental build the bar cup. + doCompile(Arrays.asList(JavaResourceBase.BAR)); + } + + public void testSingleBuild() { + List<MockResource> resources = new ArrayList<MockResource>(); + Collections.addAll(resources, JavaResourceBase.getStandardResources()); + resources.add(JavaResourceBase.FOO); + resources.add(JavaResourceBase.BAR); + doCompile(resources); + } + + private void doCompile(List<? extends MockResource> resources) { + ICompilationUnit[] icus = new ICompilationUnit[resources.size()]; + for (int i = 0; i < icus.length; ++i) { + icus[i] = new ResourceAdapter(resources.get(i)); + } + compiler.compile(icus); + } +}
diff --git a/dev/core/test/com/google/gwt/dev/javac/impl/MockJavaResource.java b/dev/core/test/com/google/gwt/dev/javac/impl/MockJavaResource.java new file mode 100644 index 0000000..2f3de7a --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/javac/impl/MockJavaResource.java
@@ -0,0 +1,23 @@ +/* + * Copyright 2008 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.impl; + +public abstract class MockJavaResource extends MockResource { + + public MockJavaResource(String qualifiedTypeName) { + super(Shared.toPath(qualifiedTypeName)); + } +}
diff --git a/dev/core/test/com/google/gwt/dev/javac/impl/MockJavaSourceFile.java b/dev/core/test/com/google/gwt/dev/javac/impl/MockJavaSourceFile.java new file mode 100644 index 0000000..1225e50 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/javac/impl/MockJavaSourceFile.java
@@ -0,0 +1,71 @@ +/* + * Copyright 2008 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.impl; + +import com.google.gwt.dev.javac.JavaSourceFile; + +public class MockJavaSourceFile extends JavaSourceFile { + + private final String location; + private final String qualifiedTypeName; + private final String source; + + public MockJavaSourceFile(JavaSourceFile sourceFile) { + this(sourceFile.getTypeName(), sourceFile.readSource(), + sourceFile.getLocation()); + } + + public MockJavaSourceFile(MockResource resource) { + this(Shared.toTypeName(resource.getPath()), + resource.getContent().toString(), resource.getLocation()); + } + + public MockJavaSourceFile(String qualifiedTypeName, String source) { + this(qualifiedTypeName, source, "/mock/" + Shared.toPath(qualifiedTypeName)); + } + + public MockJavaSourceFile(String qualifiedTypeName, String source, + String location) { + this.qualifiedTypeName = qualifiedTypeName; + this.source = source; + this.location = location; + } + + @Override + public String getLocation() { + return location; + } + + @Override + public String getPackageName() { + return Shared.getPackageName(qualifiedTypeName); + } + + @Override + public String getShortName() { + return Shared.getShortName(qualifiedTypeName); + } + + @Override + public String getTypeName() { + return qualifiedTypeName; + } + + @Override + public String readSource() { + return source; + } +}
diff --git a/dev/core/test/com/google/gwt/dev/javac/impl/MockResource.java b/dev/core/test/com/google/gwt/dev/javac/impl/MockResource.java new file mode 100644 index 0000000..a0fbc94 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/javac/impl/MockResource.java
@@ -0,0 +1,56 @@ +/* + * Copyright 2008 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.impl; + +import com.google.gwt.dev.resource.Resource; +import com.google.gwt.dev.util.Util; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.net.URL; + +/** + * An in-memory {@link Resource}. + */ +public abstract class MockResource extends Resource { + private final String path; + + public MockResource(String path) { + this.path = path; + } + + @Override + public String getLocation() { + return "/mock/" + path; + } + + @Override + public String getPath() { + return path; + } + + @Override + public URL getURL() { + return null; + } + + @Override + public InputStream openContents() { + return new ByteArrayInputStream(Util.getBytes(getContent().toString())); + } + + protected abstract CharSequence getContent(); +}
diff --git a/dev/core/test/com/google/gwt/dev/javac/impl/MockResourceOracle.java b/dev/core/test/com/google/gwt/dev/javac/impl/MockResourceOracle.java new file mode 100644 index 0000000..c224b9c --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/javac/impl/MockResourceOracle.java
@@ -0,0 +1,89 @@ +/* + * Copyright 2008 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.impl; + +import com.google.gwt.dev.resource.Resource; +import com.google.gwt.dev.resource.ResourceOracle; + +import junit.framework.Assert; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * A simple {@link ResourceOracle} for testing. + */ +public class MockResourceOracle implements ResourceOracle { + + private Map<String, Resource> exportedMap = Collections.emptyMap(); + private Set<Resource> exportedValues = Collections.emptySet(); + + public MockResourceOracle(Resource... resources) { + add(resources); + } + + public Set<String> getPathNames() { + return exportedMap.keySet(); + } + + public Map<String, Resource> getResourceMap() { + return exportedMap; + } + + public Set<Resource> getResources() { + return exportedValues; + } + + void add(Resource... resources) { + Map<String, Resource> newMap = new HashMap<String, Resource>(exportedMap); + for (Resource resource : resources) { + String path = resource.getPath(); + Assert.assertFalse(newMap.containsKey(path)); + newMap.put(path, resource); + } + export(newMap); + } + + void remove(String... paths) { + Map<String, Resource> newMap = new HashMap<String, Resource>(exportedMap); + for (String path : paths) { + Resource oldValue = newMap.remove(path); + Assert.assertNotNull(oldValue); + } + export(newMap); + } + + void replace(Resource... resources) { + Map<String, Resource> newMap = new HashMap<String, Resource>(exportedMap); + for (Resource resource : resources) { + String path = resource.getPath(); + Assert.assertTrue(newMap.containsKey(path)); + newMap.put(path, resource); + } + export(newMap); + } + + private void export(Map<String, Resource> newMap) { + exportedMap = Collections.unmodifiableMap(newMap); + // Make a new hash set for constant lookup. + exportedValues = Collections.unmodifiableSet(new HashSet<Resource>( + exportedMap.values())); + } + +} \ No newline at end of file
diff --git a/dev/core/test/com/google/gwt/dev/jdt/ByteCodeCompilerTest.java b/dev/core/test/com/google/gwt/dev/jdt/ByteCodeCompilerTest.java deleted file mode 100644 index 0f24786..0000000 --- a/dev/core/test/com/google/gwt/dev/jdt/ByteCodeCompilerTest.java +++ /dev/null
@@ -1,404 +0,0 @@ -/* - * Copyright 2008 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.jdt; - -import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider; -import com.google.gwt.dev.jdt.ByteCodeCompiler; -import com.google.gwt.dev.jdt.RebindOracle; -import com.google.gwt.dev.jdt.SourceOracle; -import com.google.gwt.dev.jdt.URLCompilationUnitProvider; -import com.google.gwt.dev.util.FileOracle; -import com.google.gwt.dev.util.FileOracleFactory; -import com.google.gwt.dev.util.FileOracleFactory.FileFilter; -import com.google.gwt.dev.util.log.Loggers; - -import junit.framework.TestCase; - -import java.net.URL; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -public class ByteCodeCompilerTest extends TestCase { - - private static class TestByteCodeCompilerHost implements SourceOracle, - RebindOracle { - - private abstract class TestCup implements CompilationUnitProvider { - - /** - * Creates a new {@code TestCup} with several types. The first type in - * {@code typeNames} is considered to be the main type. - * - * @param packageName the package for the types in this {@code TestCup} - * @param typeNames the types for this {@code TestCup}. Must have - * at least one type. The first type is considered to be the main type - * for this {@code TestCup}. - */ - public TestCup(String packageName, String... typeNames) { - this.packageName = packageName; - registerPackage(packageName); - for (int i = 0; i < typeNames.length; i++) { - if (packageName.length() > 0) { - registerType(packageName + "." + typeNames[i], this); - } else { - // In the default package. - // - registerType(typeNames[i], this); - } - } - firstTypeName = typeNames[0]; - } - - public long getLastModified() throws UnableToCompleteException { - return 0; - } - - public String getLocation() { - return "transient source for " + packageName + "." + firstTypeName; - } - - public String getMainTypeName() { - return firstTypeName; - } - - public String getPackageName() { - return packageName; - } - - public abstract char[] getSource(); - - public boolean isTransient() { - return true; - } - - private final String firstTypeName; - private final String packageName; - } - - public TestByteCodeCompilerHost() { - registerPackage(""); // the default package - } - - public final CompilationUnitProvider findCompilationUnit(TreeLogger logger, - String typeName) { - return cupsByTypeName.get(typeName); - } - - public final boolean isPackage(String possiblePackageName) { - return pkgNames.contains(possiblePackageName); - } - - // Override for specific test cases. - // - public String rebind(TreeLogger logger, String typeName) - throws UnableToCompleteException { - return typeName; - } - - public final void registerPackage(String packageName) { - String[] packageParts = packageName.split("\\."); - String toRegister = null; - for (int i = 0; i < packageParts.length; i++) { - String part = packageParts[i]; - if (toRegister != null) { - toRegister += "." + part; - } else { - toRegister = part; - } - pkgNames.add(toRegister); - } - } - - public final void registerType(String typeName, TestCup cup) { - cupsByTypeName.put(typeName, cup); - } - - { - pkgNames = new HashSet<String>(); - cupsByTypeName = new HashMap<String, CompilationUnitProvider>(); - } - - final CompilationUnitProvider CU_AB = new TestCup("test", "A", "A.B") { - public char[] getSource() { - StringBuffer sb = new StringBuffer(); - sb.append("package test;\n"); - sb.append("public class A {\n"); - sb.append(" public static class B extends A { }\n"); - sb.append("}\n"); - return sb.toString().toCharArray(); - } - }; - - final CompilationUnitProvider CU_C = new TestCup("test", "C", "C.Message") { - public char[] getSource() { - StringBuffer sb = new StringBuffer(); - sb.append("package test;\n"); - sb.append("import com.google.gwt.core.client.GWT;\n"); - sb.append("public class C {\n"); - sb.append(" public static String getMessage() {\n"); - sb.append(" return ((Message)GWT.create(Message.class)).f();\n"); - sb.append(" }\n"); - sb.append(" public static class Message {\n"); - sb.append(" public String f() {\n"); - sb.append(" return \"C.Message\";\n"); - sb.append(" }\n"); - sb.append(" }\n"); - sb.append("}\n"); - return sb.toString().toCharArray(); - } - }; - - final CompilationUnitProvider CU_CLASS = new TestCup("java.lang", "Class") { - - public char[] getSource() { - StringBuffer sb = new StringBuffer(); - sb.append("package java.lang;\n"); - sb.append("public class Class { }\n"); - return sb.toString().toCharArray(); - } - }; - - /** - * This one is different because D is not public and it lives in the default - * package. - */ - final CompilationUnitProvider CU_DE = new TestCup("", "D", "D.E") { - public char[] getSource() { - StringBuffer sb = new StringBuffer(); - sb.append("class D extends test.C.Message {\n"); - sb.append(" public static class E extends D {\n"); - sb.append(" public String getMessage() {\n"); - sb.append(" return \"D.E.Message\";\n"); - sb.append(" }\n"); - sb.append(" }\n"); - sb.append("}\n"); - return sb.toString().toCharArray(); - } - }; - - final CompilationUnitProvider CU_GWT = new TestCup( - "com.google.gwt.core.client", "GWT") { - - public char[] getSource() { - StringBuffer sb = new StringBuffer(); - sb.append("package com.google.gwt.core.client;\n"); - sb.append("public final class GWT {\n"); - sb.append(" public static Object create(Class classLiteral) { return null; }\n"); - sb.append("}\n"); - return sb.toString().toCharArray(); - } - }; - - final CompilationUnitProvider CU_MAIN = new TestCup("test", "Main") { - - public char[] getSource() { - StringBuffer sb = new StringBuffer(); - sb.append("package test;\n"); - sb.append("import com.google.gwt.core.client.GWT;\n"); - sb.append("public class Main {\n"); - sb.append(" public static void main(String[] args) {\n"); - sb.append(" A a = (A)GWT.create(A.class);\n"); - sb.append(" }\n"); - sb.append("}\n"); - return sb.toString().toCharArray(); - } - }; - - final TestCup CU_OBJECT = new TestCup("java.lang", "Object") { - public char[] getSource() { - StringBuffer sb = new StringBuffer(); - sb.append("package java.lang;\n"); - sb.append("public class Object { }\n"); - return sb.toString().toCharArray(); - } - }; - - final CompilationUnitProvider CU_STRING = new TestCup("java.lang", "String") { - - public char[] getSource() { - StringBuffer sb = new StringBuffer(); - sb.append("package java.lang;\n"); - sb.append("public class String { }\n"); - return sb.toString().toCharArray(); - } - }; - - private final Map<String, CompilationUnitProvider> cupsByTypeName; - private final Set<String> pkgNames; - } - - private static void scanAndCompile(TreeLogger logger) - throws UnableToCompleteException { - FileOracleFactory fof = new FileOracleFactory(); - fof.addPackage("", new FileFilter() { - public boolean accept(String string) { - return string.endsWith(".java"); - } - }); - final FileOracle fo = fof.create(logger); - - final SourceOracle host = new SourceOracle() { - - public CompilationUnitProvider findCompilationUnit(TreeLogger logger, - String typeName) { - // ONLY LOOK FOR TOP-LEVEL TYPES. - // - CompilationUnitProvider cup = cups.get(typeName); - if (cup == null) { - String path = typeName.replace('.', '/') + ".java"; - URL url = fo.find(path); - if (url != null) { - String pkgName = ""; - int len = findLengthOfPackagePart(typeName); - if (len > 0) { - pkgName = typeName.substring(0, len); - } - return new URLCompilationUnitProvider(url, pkgName); - } else { - return null; - } - } - return cup; - } - - public boolean isPackage(String possiblePackageName) { - String path = possiblePackageName.replace('.', '/') + "/"; - URL url = fo.find(path); - if (url != null) { - return true; - } else { - return false; - } - } - - private int findLengthOfPackagePart(String typeName) { - int maxDotIndex = 0; - int i = typeName.indexOf('.'); - while (i != -1) { - if (!isPackage(typeName.substring(0, i))) { - break; - } else { - maxDotIndex = i; - i = typeName.indexOf('.', i + 1); - } - } - return maxDotIndex; - } - - private Map<String, CompilationUnitProvider> cups = new HashMap<String, CompilationUnitProvider>(); - }; - - ByteCodeCompiler cs = new ByteCodeCompiler(host); - String[] allJava = fo.getAllFiles(); - - for (int i = 0; i < 3; ++i) { - long before = System.currentTimeMillis(); - - for (int j = 0; j < allJava.length; j++) { - String typeName = allJava[j].substring(0, allJava[j].length() - 5).replace( - '/', '.'); - cs.getClassBytes(logger, typeName); - } - - long after = System.currentTimeMillis(); - System.out.println("Iter " + i + " took " + (after - before) + " ms"); - } - } - - // This one is standalone. - // - public void testJavaLangObject() throws Exception { - ByteCodeCompiler cs = new ByteCodeCompiler(testHost); - assertNotNull(cs.getClassBytes(logger, "java.lang.Object")); - } - - // This one requires java.lang.Object. - // - public void testJavaLangString() throws Exception { - ByteCodeCompiler cs = new ByteCodeCompiler(testHost); - assertNotNull(cs.getClassBytes(logger, "java.lang.Object")); - assertNotNull(cs.getClassBytes(logger, "java.lang.String")); - } - - // Try deferred binding that takes three compile iterations. - // - In Main, we rebind from A to C - // - In C, we rebind from C to D.E - // - public void testRebindCreateTransitive() throws Exception { - ByteCodeCompiler cs = new ByteCodeCompiler(new TestByteCodeCompilerHost() { - public String rebind(TreeLogger logger, String typeName) - throws com.google.gwt.core.ext.UnableToCompleteException { - if ("test.C.Message".equals(typeName)) { - return "D.E"; - } else { - return typeName; - } - } - }); - - assertNotNull(cs.getClassBytes(logger, "test.C")); - assertNotNull(cs.getClassBytes(logger, "test.C$Message")); - assertNotNull(cs.getClassBytes(logger, "D")); - assertNotNull(cs.getClassBytes(logger, "D$E")); - - assertNotNull(cs.getClassBytes(logger, "java.lang.Object")); - assertNotNull(cs.getClassBytes(logger, "java.lang.String")); - assertNotNull(cs.getClassBytes(logger, "java.lang.Class")); - assertNotNull(cs.getClassBytes(logger, "com.google.gwt.core.client.GWT")); - } - - // Try deferred binding that works. - // - public void testRebindCreateWithSuccess() throws Exception { - ByteCodeCompiler cs = new ByteCodeCompiler(new TestByteCodeCompilerHost() { - public String rebind(TreeLogger logger, String typeName) - throws com.google.gwt.core.ext.UnableToCompleteException { - if ("test.A".equals(typeName)) { - return "test.A.B"; - } else { - return typeName; - } - } - }); - - assertNotNull(cs.getClassBytes(logger, "java.lang.Object")); - assertNotNull(cs.getClassBytes(logger, "java.lang.String")); - assertNotNull(cs.getClassBytes(logger, "java.lang.Class")); - assertNotNull(cs.getClassBytes(logger, "com.google.gwt.core.client.GWT")); - - assertNotNull(cs.getClassBytes(logger, "test.Main")); - assertNotNull(cs.getClassBytes(logger, "test.A")); - assertNotNull(cs.getClassBytes(logger, "test.A$B")); - - // Check again for the same class to make sure it's cached. - // (Although you have to run this test with "-Dgwt.useGuiLogger" defined - // to see what it does.) - // - assertNotNull(cs.getClassBytes(logger, "java.lang.Object")); - } - - protected void setUp() throws Exception { - logger = Loggers.createOptionalGuiTreeLogger(); - testHost = new TestByteCodeCompilerHost(); - } - - private TreeLogger logger = TreeLogger.NULL; - private TestByteCodeCompilerHost testHost = null; -}
diff --git a/dev/core/test/com/google/gwt/dev/jdt/TypeOracleTestingUtils.java b/dev/core/test/com/google/gwt/dev/jdt/TypeOracleTestingUtils.java deleted file mode 100644 index 1b68679..0000000 --- a/dev/core/test/com/google/gwt/dev/jdt/TypeOracleTestingUtils.java +++ /dev/null
@@ -1,123 +0,0 @@ -/* - * Copyright 2008 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.jdt; - -import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider; - -/** - * Utilities for tests that build a type oracle and watch for errors. - * - */ -public class TypeOracleTestingUtils { - public static void addCup(TypeOracleBuilder builder, String typeName, - CharSequence code) throws UnableToCompleteException { - CompilationUnitProvider cup = createCup(typeName, code); - builder.addCompilationUnit(cup); - } - - public static CompilationUnitProvider createCup(String typeName, - CharSequence code) { - String packageName; - String className; - int pos = typeName.lastIndexOf('.'); - if (pos >= 0) { - packageName = typeName.substring(0, pos); - className = typeName.substring(pos + 1); - } else { - packageName = ""; - className = typeName; - } - StaticCompilationUnitProvider cup = new StaticCompilationUnitProvider( - packageName, className, code.toString().toCharArray()); - return cup; - } - - /** - * Add compilation units for basic classes like Object and String. - */ - public static void addStandardCups(TypeOracleBuilder builder) - throws UnableToCompleteException { - { - StringBuffer code = new StringBuffer(); - code.append("package java.lang;\n"); - code.append("public class Object {\n"); - code.append(" public String toString() { return \"Object\"; }\n"); - code.append(" public Object clone() { return this; } "); - code.append("}\n"); - addCup(builder, "java.lang.Object", code); - } - { - StringBuffer code = new StringBuffer(); - code.append("package java.lang;\n"); - code.append("public class Class<T> {\n"); - code.append("}\n"); - addCup(builder, "java.lang.Class", code); - } - { - StringBuffer code = new StringBuffer(); - code.append("package java.lang;\n"); - code.append("public final class String {\n"); - code.append(" public int length() { return 0; }\n"); - code.append("}\n"); - addCup(builder, "java.lang.String", code); - } - { - StringBuffer code = new StringBuffer(); - code.append("package java.lang;\n"); - code.append("public interface Serializable { }\n"); - addCup(builder, "java.lang.Serializable", code); - } - { - StringBuffer code = new StringBuffer(); - code.append("package java.util;\n"); - code.append("public interface Map<K,V> { }\n"); - addCup(builder, "java.util.Map", code); - } - { - StringBuffer code = new StringBuffer(); - code.append("package java.lang;\n"); - code.append("public @interface SuppressWarnings {\n"); - code.append(" String[] value();\n"); - code.append("}\n"); - addCup(builder, "java.lang.SuppressWarnings", code); - } - { - StringBuffer code = new StringBuffer(); - code.append("package java.lang.annotation;\n"); - code.append("public interface Annotation {\n"); - code.append("}\n"); - addCup(builder, "java.lang.annotation.Annotation", code); - } - { - StringBuffer code = new StringBuffer(); - code.append("package com.google.gwt.core.client;\n"); - code.append("public class JavaScriptObject {\n"); - code.append(" protected JavaScriptObject() { }\n"); - code.append("}\n"); - addCup(builder, "com.google.gwt.core.client.JavaScriptObject", code); - } - } - - public static void buildTypeOracleForCode(String typeName, CharSequence code, - TreeLogger testLogger) throws UnableToCompleteException { - TypeOracleBuilder builder = new TypeOracleBuilder(); - addStandardCups(builder); - addCup(builder, typeName, code); - builder.build(testLogger); - } -}
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/AbstractResourceOrientedTestBase.java b/dev/core/test/com/google/gwt/dev/resource/impl/AbstractResourceOrientedTestBase.java new file mode 100644 index 0000000..6d5e3ee --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/resource/impl/AbstractResourceOrientedTestBase.java
@@ -0,0 +1,208 @@ +/* + * Copyright 2008 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.resource.impl; + +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.dev.resource.Resource; +import com.google.gwt.dev.util.log.PrintWriterTreeLogger; + +import junit.framework.TestCase; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.HashSet; +import java.util.Set; +import java.util.jar.JarFile; + +/** + * Shared abstract class for tests that rely on well-known test data. + * + * These tests rely on the external existence of the following files under + * <code>test/com/google/gwt/dev/javac/impl/testdata/</code> + * + * <pre> + * cpe1/com/google/gwt/user/User.gwt.xml + * cpe1/com/google/gwt/user/client/Command.java + * cpe1/com/google/gwt/user/client/Timer.java + * cpe1/com/google/gwt/user/client/ui/Widget.java + * cpe1/org/example/bar/client/BarClient1.txt + * cpe1/org/example/bar/client/BarClient2.txt + * cpe1/org/example/bar/client/etc/BarEtc.txt + * cpe1/org/example/foo/client/FooClient.java + * cpe1/org/example/foo/server/FooServer.java + * + * cpe1/com/google/gwt/i18n/I18N.gwt.xml + * cpe2/com/google/gwt/i18n/client/Messages.java + * cpe2/com/google/gwt/i18n/rebind/LocalizableGenerator.java + * cpe2/org/example/bar/client/BarClient2.txt + * cpe2/org/example/bar/client/BarClient3.txt + * </pre> + * + * The same files should be present in jar files named cpe1.jar and cpe2.jar; + * note that the contents of each will not have the <code>cpe1/</code> and + * <code>cpe2/</code> prefixes (respectively) on their contained files. + */ +public abstract class AbstractResourceOrientedTestBase extends TestCase { + + private static class ExcludeSvnClassPathEntry extends ClassPathEntry { + private final ClassPathEntry cpe; + + public ExcludeSvnClassPathEntry(ClassPathEntry cpe) { + this.cpe = cpe; + } + + @Override + public Set<AbstractResource> findApplicableResources(TreeLogger logger, + PathPrefixSet pathPrefixSet) { + Set<AbstractResource> results = new HashSet<AbstractResource>(); + Set<AbstractResource> rs = cpe.findApplicableResources(logger, + pathPrefixSet); + for (AbstractResource r : rs) { + if (r.getPath().indexOf(".svn/") < 0) { + results.add(r); + } + } + return results; + } + + @Override + public String getLocation() { + return cpe.getLocation(); + } + + @Override + public String toString() { + return cpe.toString(); + } + } + + private static class MOCK_CPE1 extends MockClassPathEntry { + public MOCK_CPE1() { + super("/cpe1/"); + addResource("com/google/gwt/user/User.gwt.xml"); + addResource("com/google/gwt/user/client/Command.java"); + addResource("com/google/gwt/user/client/Timer.java"); + addResource("com/google/gwt/user/client/ui/Widget.java"); + addResource("org/example/bar/client/BarClient1.txt"); + addResource("org/example/bar/client/BarClient2.txt"); + addResource("org/example/bar/client/etc/BarEtc.txt"); + addResource("org/example/foo/client/FooClient.java"); + addResource("org/example/foo/server/FooServer.java"); + } + } + + private static class MOCK_CPE2 extends MockClassPathEntry { + public MOCK_CPE2() { + super("C:\\cpe2"); + addResource("com/google/gwt/i18n/I18N.gwt.xml"); + addResource("com/google/gwt/i18n/client/Messages.java"); + addResource("com/google/gwt/i18n/rebind/LocalizableGenerator.java"); + addResource("org/example/bar/client/BarClient2.txt"); + addResource("org/example/bar/client/BarClient3.txt"); + } + } + + // Set LOG_TO_CONSOLE to true to see a play-by-play. + private static final boolean LOG_TO_CONSOLE = false; + + public static TreeLogger createTestTreeLogger() { + if (LOG_TO_CONSOLE) { + PrintWriterTreeLogger treeLogger = new PrintWriterTreeLogger(); + treeLogger.setMaxDetail(TreeLogger.ALL); + treeLogger.log(TreeLogger.INFO, "=== logger start ==="); + return treeLogger; + } else { + return TreeLogger.NULL; + } + } + + protected void assertPathIncluded(Set<AbstractResource> resources, String path) { + assertNotNull(findResourceWithPath(resources, path)); + } + + protected void assertPathNotIncluded(Set<AbstractResource> resources, + String path) { + assertNull(findResourceWithPath(resources, path)); + } + + protected File findJarDirectory(String name) throws URISyntaxException { + ClassLoader classLoader = getClass().getClassLoader(); + URL url = classLoader.getResource(name); + assertNotNull("Expecting on the classpath: " + name); + File file = new File(url.toURI()); + assertTrue("Cannot read as file: " + url.toExternalForm(), file.canRead()); + return file; + } + + protected File findJarFile(String name) throws URISyntaxException { + ClassLoader classLoader = getClass().getClassLoader(); + URL url = classLoader.getResource(name); + assertNotNull( + "Expecting on the classpath: " + + name + + "; did you forget to put the source root containing this very source file to the classpath?", + url); + File file = new File(url.toURI()); + assertTrue("Cannot read as file: " + url.toExternalForm(), file.canRead()); + return file; + } + + protected Resource findResourceWithPath(Set<AbstractResource> resources, + String path) { + for (Resource r : resources) { + if (r.getPath().equals(path)) { + return r; + } + } + return null; + } + + protected ClassPathEntry getClassPathEntry1AsDirectory() + throws URISyntaxException { + File dir = findJarDirectory("com/google/gwt/dev/resource/impl/testdata/cpe1"); + return new ExcludeSvnClassPathEntry(new DirectoryClassPathEntry(dir)); + } + + protected ClassPathEntry getClassPathEntry1AsJar() throws IOException, + URISyntaxException { + File file = findJarFile("com/google/gwt/dev/resource/impl/testdata/cpe1.jar"); + return new ExcludeSvnClassPathEntry(new JarFileClassPathEntry(new JarFile( + file))); + } + + protected ClassPathEntry getClassPathEntry1AsMock() { + return new ExcludeSvnClassPathEntry(new MOCK_CPE1()); + } + + protected ClassPathEntry getClassPathEntry2AsDirectory() + throws URISyntaxException { + File dir = findJarDirectory("com/google/gwt/dev/resource/impl/testdata/cpe2"); + return new ExcludeSvnClassPathEntry(new DirectoryClassPathEntry(dir)); + } + + protected ClassPathEntry getClassPathEntry2AsJar() throws URISyntaxException, + IOException { + File file = findJarFile("com/google/gwt/dev/resource/impl/testdata/cpe2.jar"); + return new ExcludeSvnClassPathEntry(new JarFileClassPathEntry(new JarFile( + file))); + } + + protected ClassPathEntry getClassPathEntry2AsMock() { + return new ExcludeSvnClassPathEntry(new MOCK_CPE2()); + } +}
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/ClassPathEntryTest.java b/dev/core/test/com/google/gwt/dev/resource/impl/ClassPathEntryTest.java new file mode 100644 index 0000000..3454176 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/resource/impl/ClassPathEntryTest.java
@@ -0,0 +1,262 @@ +/* + * Copyright 2008 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.resource.impl; + +import com.google.gwt.core.ext.TreeLogger; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.Set; + +public class ClassPathEntryTest extends AbstractResourceOrientedTestBase { + + public void testAllCpe1FilesFound() throws URISyntaxException, IOException { + testAllCpe1FilesFound(getClassPathEntry1AsJar()); + testAllCpe1FilesFound(getClassPathEntry1AsDirectory()); + } + + public void testAllCpe2FilesFound() throws URISyntaxException, IOException { + testAllCpe2FilesFound(getClassPathEntry2AsJar()); + testAllCpe2FilesFound(getClassPathEntry2AsDirectory()); + } + + public void testPathPrefixSetChanges() throws IOException, URISyntaxException { + ClassPathEntry cpe1jar = getClassPathEntry1AsJar(); + ClassPathEntry cpe1dir = getClassPathEntry1AsDirectory(); + ClassPathEntry cpe2jar = getClassPathEntry2AsJar(); + ClassPathEntry cpe2dir = getClassPathEntry2AsDirectory(); + + testPathPrefixSetChanges(cpe1jar, cpe2jar); + testPathPrefixSetChanges(cpe1dir, cpe2dir); + testPathPrefixSetChanges(cpe1jar, cpe2dir); + testPathPrefixSetChanges(cpe1dir, cpe2jar); + } + + public void testUseOfPrefixesWithFiltering() throws IOException, + URISyntaxException { + ClassPathEntry cpe1jar = getClassPathEntry1AsJar(); + ClassPathEntry cpe1dir = getClassPathEntry1AsDirectory(); + ClassPathEntry cpe2jar = getClassPathEntry2AsJar(); + ClassPathEntry cpe2dir = getClassPathEntry2AsDirectory(); + + testUseOfPrefixesWithFiltering(cpe1jar, cpe2jar); + testUseOfPrefixesWithFiltering(cpe1dir, cpe2dir); + testUseOfPrefixesWithFiltering(cpe1jar, cpe2dir); + testUseOfPrefixesWithFiltering(cpe1dir, cpe2jar); + } + + public void testUseOfPrefixesWithoutFiltering() throws URISyntaxException, + IOException { + ClassPathEntry cpe1jar = getClassPathEntry1AsJar(); + ClassPathEntry cpe1dir = getClassPathEntry1AsDirectory(); + ClassPathEntry cpe2jar = getClassPathEntry2AsJar(); + ClassPathEntry cpe2dir = getClassPathEntry2AsDirectory(); + + testUseOfPrefixesWithoutFiltering(cpe1jar, cpe2jar); + testUseOfPrefixesWithoutFiltering(cpe1dir, cpe2dir); + testUseOfPrefixesWithoutFiltering(cpe1jar, cpe2dir); + testUseOfPrefixesWithoutFiltering(cpe1dir, cpe2jar); + } + + public void testUseOfPrefixesWithoutFiltering(ClassPathEntry cpe1, + ClassPathEntry cpe2) { + TreeLogger logger = createTestTreeLogger(); + + PathPrefixSet pps = new PathPrefixSet(); + pps.add(new PathPrefix("com/google/gwt/user/client/", null)); + pps.add(new PathPrefix("com/google/gwt/i18n/client/", null)); + + { + // Examine cpe1. + Set<AbstractResource> r = cpe1.findApplicableResources(logger, pps); + + assertEquals(3, r.size()); + assertPathIncluded(r, "com/google/gwt/user/client/Command.java"); + assertPathIncluded(r, "com/google/gwt/user/client/Timer.java"); + assertPathIncluded(r, "com/google/gwt/user/client/ui/Widget.java"); + } + + { + // Examine cpe2. + Set<AbstractResource> r = cpe2.findApplicableResources(logger, pps); + + assertEquals(1, r.size()); + assertPathIncluded(r, "com/google/gwt/i18n/client/Messages.java"); + } + } + + // NOTE: if this test fails, ensure that the source root containing this very + // source file is *FIRST* on the classpath + private void testAllCpe1FilesFound(ClassPathEntry cpe1) { + TreeLogger logger = createTestTreeLogger(); + + PathPrefixSet pps = new PathPrefixSet(); + pps.add(new PathPrefix("", null)); + + Set<AbstractResource> r = cpe1.findApplicableResources(logger, pps); + + assertEquals(9, r.size()); + assertPathIncluded(r, "com/google/gwt/user/User.gwt.xml"); + assertPathIncluded(r, "com/google/gwt/user/client/Command.java"); + assertPathIncluded(r, "com/google/gwt/user/client/Timer.java"); + assertPathIncluded(r, "com/google/gwt/user/client/ui/Widget.java"); + assertPathIncluded(r, "org/example/bar/client/BarClient1.txt"); + assertPathIncluded(r, "org/example/bar/client/BarClient2.txt"); + assertPathIncluded(r, "org/example/bar/client/etc/BarEtc.txt"); + assertPathIncluded(r, "org/example/foo/client/FooClient.java"); + assertPathIncluded(r, "org/example/foo/server/FooServer.java"); + } + + // NOTE: if this test fails, ensure that the source root containing this very + // source file is on the classpath + private void testAllCpe2FilesFound(ClassPathEntry cpe2) { + TreeLogger logger = createTestTreeLogger(); + + PathPrefixSet pps = new PathPrefixSet(); + pps.add(new PathPrefix("", null)); + Set<AbstractResource> r = cpe2.findApplicableResources(logger, pps); + + assertEquals(5, r.size()); + assertPathIncluded(r, "com/google/gwt/i18n/I18N.gwt.xml"); + assertPathIncluded(r, "com/google/gwt/i18n/client/Messages.java"); + assertPathIncluded(r, + "com/google/gwt/i18n/rebind/LocalizableGenerator.java"); + assertPathIncluded(r, "org/example/bar/client/BarClient2.txt"); + assertPathIncluded(r, "org/example/bar/client/BarClient3.txt"); + } + + private void testPathPrefixSetChanges(ClassPathEntry cpe1, ClassPathEntry cpe2) { + TreeLogger logger = createTestTreeLogger(); + + { + // Filter is not set yet. + PathPrefixSet pps = new PathPrefixSet(); + pps.add(new PathPrefix("com/google/gwt/user/", null)); + pps.add(new PathPrefix("com/google/gwt/i18n/", null)); + + // Examine cpe1 in the absence of the filter. + Set<AbstractResource> r1 = cpe1.findApplicableResources(logger, pps); + + assertEquals(4, r1.size()); + assertPathIncluded(r1, "com/google/gwt/user/User.gwt.xml"); + assertPathIncluded(r1, "com/google/gwt/user/client/Command.java"); + assertPathIncluded(r1, "com/google/gwt/user/client/Timer.java"); + assertPathIncluded(r1, "com/google/gwt/user/client/ui/Widget.java"); + + // Examine cpe2 in the absence of the filter. + Set<AbstractResource> r2 = cpe2.findApplicableResources(logger, pps); + + assertEquals(3, r2.size()); + assertPathIncluded(r2, "com/google/gwt/i18n/I18N.gwt.xml"); + assertPathIncluded(r2, "com/google/gwt/i18n/client/Messages.java"); + assertPathIncluded(r2, + "com/google/gwt/i18n/rebind/LocalizableGenerator.java"); + } + + { + // Create a pps with a filter. + ResourceFilter excludeXmlFiles = new ResourceFilter() { + public boolean allows(String path) { + return !path.endsWith(".xml"); + } + }; + + PathPrefixSet pps = new PathPrefixSet(); + pps.add(new PathPrefix("com/google/gwt/user/", excludeXmlFiles)); + pps.add(new PathPrefix("com/google/gwt/i18n/", excludeXmlFiles)); + + // Examine cpe1 in the presence of the filter. + Set<AbstractResource> r1 = cpe1.findApplicableResources(logger, pps); + + assertEquals(3, r1.size()); + assertPathNotIncluded(r1, "com/google/gwt/user/User.gwt.xml"); + assertPathIncluded(r1, "com/google/gwt/user/client/Command.java"); + assertPathIncluded(r1, "com/google/gwt/user/client/Timer.java"); + assertPathIncluded(r1, "com/google/gwt/user/client/ui/Widget.java"); + + // Examine cpe2 in the presence of the filter. + Set<AbstractResource> r2 = cpe2.findApplicableResources(logger, pps); + + assertEquals(2, r2.size()); + assertPathNotIncluded(r1, "com/google/gwt/user/User.gwt.xml"); + assertPathIncluded(r2, "com/google/gwt/i18n/client/Messages.java"); + assertPathIncluded(r2, + "com/google/gwt/i18n/rebind/LocalizableGenerator.java"); + } + + { + /* + * Change the prefix path set to the zero-lenth prefix (which allows + * everything), but specify a filter that disallows everything. + */ + PathPrefixSet pps = new PathPrefixSet(); + pps.add(new PathPrefix("", new ResourceFilter() { + public boolean allows(String path) { + // Exclude everything. + return false; + } + })); + + // Examine cpe1 in the presence of the filter. + Set<AbstractResource> r1 = cpe1.findApplicableResources(logger, pps); + + assertEquals(0, r1.size()); + + // Examine cpe2 in the presence of the filter. + Set<AbstractResource> r2 = cpe2.findApplicableResources(logger, pps); + + assertEquals(0, r2.size()); + } + } + + private void testUseOfPrefixesWithFiltering(ClassPathEntry cpe1, + ClassPathEntry cpe2) { + TreeLogger logger = createTestTreeLogger(); + + PathPrefixSet pps = new PathPrefixSet(); + ResourceFilter excludeXmlFiles = new ResourceFilter() { + public boolean allows(String path) { + return !path.endsWith(".xml"); + } + }; + // The prefix is intentionally starting at the module-level, not 'client'. + pps.add(new PathPrefix("com/google/gwt/user/", excludeXmlFiles)); + pps.add(new PathPrefix("com/google/gwt/i18n/", excludeXmlFiles)); + + { + // Examine cpe1. + Set<AbstractResource> r = cpe1.findApplicableResources(logger, pps); + + assertEquals(3, r.size()); + // User.gwt.xml would be included but for the filter. + assertPathIncluded(r, "com/google/gwt/user/client/Command.java"); + assertPathIncluded(r, "com/google/gwt/user/client/Timer.java"); + assertPathIncluded(r, "com/google/gwt/user/client/ui/Widget.java"); + } + + { + // Examine cpe2. + Set<AbstractResource> r = cpe2.findApplicableResources(logger, pps); + + assertEquals(2, r.size()); + // I18N.gwt.xml would be included but for the filter. + assertPathIncluded(r, "com/google/gwt/i18n/client/Messages.java"); + assertPathIncluded(r, + "com/google/gwt/i18n/rebind/LocalizableGenerator.java"); + } + } + +}
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/FileResourceTest.java b/dev/core/test/com/google/gwt/dev/resource/impl/FileResourceTest.java new file mode 100644 index 0000000..7a38934 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/resource/impl/FileResourceTest.java
@@ -0,0 +1,96 @@ +/* + * Copyright 2008 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.resource.impl; + +import com.google.gwt.dev.util.Util; + +import junit.framework.TestCase; + +import java.io.File; +import java.io.IOException; + +public class FileResourceTest extends TestCase { + + public void testDeletion() { + File f = null; + try { + f = File.createTempFile("com.google.gwt.dev.javac.impl.FileResourceTest", + ".tmp"); + f.deleteOnExit(); + Util.writeStringAsFile(f, "contents 1"); + } catch (IOException e) { + fail("Failed to create test file"); + } + + File dir = f.getParentFile(); + DirectoryClassPathEntry cpe = new DirectoryClassPathEntry(dir); + FileResource r = new FileResource(cpe, f.getName(), f); + assertEquals(f.getAbsoluteFile().toURI().toString(), r.getLocation()); + + /* + * In this case, there's no subdirectory, so the path should match the + * simple filename. + */ + assertEquals(f.getName(), r.getPath()); + + // Shouldn't be stale yet. + assertFalse(r.isStale()); + + /* + * Touch the file at more than one second to ensure there's a noticeable + * difference on every platform. + */ + f.setLastModified(f.lastModified() + 1500); + + // Should be stale now. + assertTrue(r.isStale()); + } + + public void testModification() { + File f = null; + try { + f = File.createTempFile("com.google.gwt.dev.javac.impl.FileResourceTest", + ".tmp"); + f.deleteOnExit(); + Util.writeStringAsFile(f, "contents 1"); + } catch (IOException e) { + fail("Failed to create test file"); + } + + File dir = f.getParentFile(); + DirectoryClassPathEntry cpe = new DirectoryClassPathEntry(dir); + FileResource r = new FileResource(cpe, f.getName(), f); + assertEquals(f.getAbsoluteFile().toURI().toString(), r.getLocation()); + + /* + * In this case, there's no subdirectory, so the path should match the + * simple filename. + */ + assertEquals(f.getName(), r.getPath()); + + // Shouldn't be stale yet. + assertFalse(r.isStale()); + + // Delete the file. + f.delete(); + + // Should be stale now. + assertTrue(r.isStale()); + + // Get can't contents anymore, either. + assertNull(r.openContents()); + } +}
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/MockAbstractResource.java b/dev/core/test/com/google/gwt/dev/resource/impl/MockAbstractResource.java new file mode 100644 index 0000000..25de30f --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/resource/impl/MockAbstractResource.java
@@ -0,0 +1,67 @@ +/* + * Copyright 2008 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.resource.impl; + +import junit.framework.Assert; + +import java.io.InputStream; +import java.net.URL; + +public final class MockAbstractResource extends AbstractResource { + private boolean isStale; + private final MockClassPathEntry mockClassPathEntry; + private final String path; + + public MockAbstractResource(MockClassPathEntry mockClassPathEntry, String path) { + this.mockClassPathEntry = mockClassPathEntry; + this.path = path; + } + + @Override + public ClassPathEntry getClassPathEntry() { + return this.mockClassPathEntry; + } + + @Override + public String getLocation() { + return this.mockClassPathEntry.pathRoot + "/" + path; + } + + @Override + public String getPath() { + return path; + } + + @Override + public URL getURL() { + return null; + } + + @Override + public boolean isStale() { + return isStale; + } + + @Override + public InputStream openContents() { + Assert.fail("Not implemented"); + return null; + } + + public void setStale(boolean isStale) { + this.isStale = isStale; + } +} \ No newline at end of file
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/MockClassPathEntry.java b/dev/core/test/com/google/gwt/dev/resource/impl/MockClassPathEntry.java new file mode 100644 index 0000000..913b071 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/resource/impl/MockClassPathEntry.java
@@ -0,0 +1,89 @@ +/* + * Copyright 2008 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.resource.impl; + +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.dev.resource.Resource; + +import junit.framework.Assert; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class MockClassPathEntry extends ClassPathEntry { + + final String pathRoot; + private final Map<String, MockAbstractResource> resourceMap = new HashMap<String, MockAbstractResource>(); + + /** + * By default, MockClassPathEntry has an all-inclusive path prefix. Tests may + * change it by calling {@link #setPathPrefixes(PathPrefixSet)}. + */ + public MockClassPathEntry(String pathRoot) { + this.pathRoot = pathRoot; + } + + public void addResource(String resourcePath) { + Resource old = resourceMap.get(resourcePath); + Assert.assertNull( + "resource already exists; use updateResource() to replace", old); + resourceMap.put(resourcePath, createMockResource(resourcePath)); + } + + @Override + public Set<AbstractResource> findApplicableResources(TreeLogger logger, + PathPrefixSet pathPrefixes) { + // Only include resources that have the prefix and pass its filter. + HashSet<AbstractResource> results = new HashSet<AbstractResource>(); + for (Map.Entry<String, MockAbstractResource> entry : resourceMap.entrySet()) { + String path = entry.getKey(); + if (pathPrefixes.includesResource(path)) { + results.add(entry.getValue()); + } + } + + return results; + } + + @Override + public String getLocation() { + return pathRoot; + } + + public void removeResource(String resourcePath) { + Resource old = resourceMap.get(resourcePath); + Assert.assertNotNull( + "resource does not already exists; use addResource() to add it first", + old); + resourceMap.remove(resourcePath); + } + + public void updateResource(String resourcePath) { + MockAbstractResource old = resourceMap.get(resourcePath); + Assert.assertNotNull( + "resource does not already exists; use addResource() if you were trying to add", + old); + old.setStale(true); + resourceMap.put(resourcePath, createMockResource(resourcePath)); + } + + private MockAbstractResource createMockResource(final String path) { + return new MockAbstractResource(this, path); + } + +}
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/PathPrefixSetTest.java b/dev/core/test/com/google/gwt/dev/resource/impl/PathPrefixSetTest.java new file mode 100644 index 0000000..e9d581c --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/resource/impl/PathPrefixSetTest.java
@@ -0,0 +1,177 @@ +/* + * Copyright 2008 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.resource.impl; + +import junit.framework.TestCase; + +/** + * Tests the trie and filtering behavior of path prefix set. + */ +public class PathPrefixSetTest extends TestCase { + + public void testEmptyPrefixSet() { + PathPrefixSet pps = new PathPrefixSet(); + assertFalse(pps.includesResource("com/google/gwt/user/client/Command.java")); + } + + public void testNonOverlappingPrefixes() { + { + /* + * Test with null filters to ensure nothing gets filtered out. + */ + PathPrefixSet pps = new PathPrefixSet(); + pps.add(new PathPrefix("com/google/gwt/user/client/", null)); + pps.add(new PathPrefix("com/google/gwt/i18n/client/", null)); + pps.add(new PathPrefix("com/google/gwt/dom/client/", null)); + + assertTrue(pps.includesDirectory("com/")); + assertTrue(pps.includesDirectory("com/google/")); + assertTrue(pps.includesDirectory("com/google/gwt/")); + assertTrue(pps.includesDirectory("com/google/gwt/user/")); + assertTrue(pps.includesDirectory("com/google/gwt/user/client/")); + assertTrue(pps.includesDirectory("com/google/gwt/user/client/ui/")); + + assertFalse(pps.includesDirectory("org/")); + assertFalse(pps.includesDirectory("org/example/")); + assertFalse(pps.includesDirectory("com/google/gwt/user/server/")); + assertFalse(pps.includesDirectory("com/google/gwt/xml/client/")); + + assertTrue(pps.includesResource("com/google/gwt/user/client/Command.java")); + assertTrue(pps.includesResource("com/google/gwt/user/client/Timer.java")); + assertTrue(pps.includesResource("com/google/gwt/i18n/client/Messages.java")); + assertTrue(pps.includesResource("com/google/gwt/dom/client/DivElement.java")); + + assertFalse(pps.includesResource("com/google/gwt/user/rebind/rpc/ServiceInterfaceProxyGenerator.java")); + assertFalse(pps.includesResource("com/google/gwt/sample/hello/client/Hello.java")); + assertFalse(pps.includesResource("com/google/gwt/user/public/clear.cache.gif")); + } + + { + /* + * Test with a real filter to ensure it does have an effect. + */ + PathPrefixSet pps = new PathPrefixSet(); + ResourceFilter allowsGifs = new ResourceFilter() { + public boolean allows(String path) { + return path.toLowerCase().endsWith(".gif"); + } + }; + + pps.add(new PathPrefix("com/google/gwt/user/public/", allowsGifs)); + pps.add(new PathPrefix("com/google/gwt/sample/mail/public/", allowsGifs)); + + // Correct prefix, and filter should allow . + assertTrue(pps.includesResource("com/google/gwt/user/public/clear.cache.gif")); + assertTrue(pps.includesResource("com/google/gwt/sample/mail/public/inboxIcon.gif")); + + // Correct prefix, but filter should exclude. + assertFalse(pps.includesResource("com/google/gwt/user/public/README.txt")); + assertFalse(pps.includesResource("com/google/gwt/sample/mail/public/README.txt")); + + // Wrong prefix, and filter would have excluded. + assertFalse(pps.includesResource("com/google/gwt/user/client/Command.java")); + assertFalse(pps.includesResource("com/google/gwt/user/rebind/rpc/ServiceInterfaceProxyGenerator.java")); + + // Wrong prefix, but filter would have allowed it. + assertFalse(pps.includesResource("com/google/gwt/i18n/public/flags.gif")); + } + } + + public void testOverlappingPrefixes() { + { + /* + * Without a filter. + */ + PathPrefixSet pps = new PathPrefixSet(); + pps.add(new PathPrefix("", null)); + pps.add(new PathPrefix("a/", null)); + pps.add(new PathPrefix("a/b/", null)); + pps.add(new PathPrefix("a/b/c/", null)); + + assertTrue(pps.includesResource("W.java")); + assertTrue(pps.includesResource("a/X.java")); + assertTrue(pps.includesResource("a/b/Y.java")); + assertTrue(pps.includesResource("a/b/c/Z.java")); + assertTrue(pps.includesResource("a/b/c/d/V.java")); + } + + { + /* + * Ensure the right filter applies. + */ + PathPrefixSet pps = new PathPrefixSet(); + pps.add(new PathPrefix("", null)); + pps.add(new PathPrefix("a/", null)); + pps.add(new PathPrefix("a/b/", new ResourceFilter() { + public boolean allows(String path) { + // Disallow anything ending with "FILTERMEOUT". + return !path.endsWith("FILTERMEOUT"); + } + })); + pps.add(new PathPrefix("a/b/c/", null)); + + assertTrue(pps.includesResource("W.java")); + assertTrue(pps.includesResource("a/X.java")); + assertTrue(pps.includesResource("a/b/Y.java")); + // This should be gone, since it is found in b. + assertFalse(pps.includesResource("a/b/FILTERMEOUT")); + /* + * This should not be gone, because it is using c's (null) filter instead + * of b's. The logic here is that the prefix including c is more specific + * and seemed to want c's resources to be included. + */ + assertTrue(pps.includesResource("a/b/c/DONT_FILTERMEOUT")); + assertTrue(pps.includesResource("a/b/c/Z.java")); + assertTrue(pps.includesResource("a/b/c/d/V.java")); + } + } + + /** + * In essense, this tests support for the default package in Java. + */ + public void testZeroLengthPrefix() { + { + /* + * Without a filter. + */ + PathPrefixSet pps = new PathPrefixSet(); + pps.add(new PathPrefix("", null)); + + assertTrue(pps.includesResource("W.java")); + assertTrue(pps.includesResource("a/X.java")); + assertTrue(pps.includesResource("a/b/Y.java")); + assertTrue(pps.includesResource("a/b/c/Z.java")); + } + + { + /* + * With a filter. + */ + PathPrefixSet pps = new PathPrefixSet(); + pps.add(new PathPrefix("", new ResourceFilter() { + public boolean allows(String path) { + return path.endsWith("Y.java"); + } + })); + + assertFalse(pps.includesResource("W.java")); + assertFalse(pps.includesResource("a/X.java")); + assertTrue(pps.includesResource("a/b/Y.java")); + assertFalse(pps.includesResource("a/b/c/Z.java")); + + } + } +}
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/ResourceOracleImplRealClasspathTest.java b/dev/core/test/com/google/gwt/dev/resource/impl/ResourceOracleImplRealClasspathTest.java new file mode 100644 index 0000000..2d228bc --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/resource/impl/ResourceOracleImplRealClasspathTest.java
@@ -0,0 +1,110 @@ +/* + * Copyright 2008 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.resource.impl; + +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.dev.resource.Resource; + +import java.util.Map; + +/** + * Tests {@link ResourceOracleImpl} using the real class path. + * + */ +public class ResourceOracleImplRealClasspathTest extends + AbstractResourceOrientedTestBase { + + private static final PathPrefix JUNIT_PREFIX = new PathPrefix( + "junit/framework/", new ResourceFilter() { + public boolean allows(String path) { + return path.endsWith("TestCase.class"); + } + }); + private static final PathPrefix JUNIT_PREFIX_DUP = new PathPrefix( + "junit/framework/", new ResourceFilter() { + public boolean allows(String path) { + return path.endsWith("TestCase.class"); + } + }); + + private static final PathPrefix THIS_CLASS_PREFIX = new PathPrefix( + "com/google/gwt/dev/resource/impl/", new ResourceFilter() { + public boolean allows(String path) { + return path.endsWith("ResourceOracleImplRealClasspathTest.class"); + } + }); + + private static final PathPrefix THIS_CLASS_PREFIX_PLUS = new PathPrefix( + "com/google/gwt/dev/resource/impl/", new ResourceFilter() { + public boolean allows(String path) { + return path.endsWith("ResourceOracleImpl.class") + || path.endsWith("ResourceOracleImplRealClasspathTest.class"); + } + }); + + private final TreeLogger logger = createTestTreeLogger(); + private final ResourceOracleImpl resourceOracle = new ResourceOracleImpl( + logger); + + public void testBasic() { + PathPrefixSet pathPrefixSet = new PathPrefixSet(); + pathPrefixSet.add(JUNIT_PREFIX); + pathPrefixSet.add(THIS_CLASS_PREFIX); + resourceOracle.setPathPrefixes(pathPrefixSet); + resourceOracle.refresh(logger); + Map<String, Resource> resourceMap = resourceOracle.getResourceMap(); + assertEquals(2, resourceMap.size()); + } + + public void testRefresh() { + PathPrefixSet pathPrefixSet = new PathPrefixSet(); + pathPrefixSet.add(JUNIT_PREFIX); + pathPrefixSet.add(THIS_CLASS_PREFIX); + resourceOracle.setPathPrefixes(pathPrefixSet); + resourceOracle.refresh(logger); + Map<String, Resource> resourceMap = resourceOracle.getResourceMap(); + assertEquals(2, resourceMap.size()); + + // Plain refresh should have no effect. + resourceOracle.refresh(logger); + assertSame(resourceMap, resourceOracle.getResourceMap()); + + // Setting same path entries should have no effect. + resourceOracle.setPathPrefixes(pathPrefixSet); + resourceOracle.refresh(logger); + assertSame(resourceMap, resourceOracle.getResourceMap()); + + // Setting identical path entries should have no effect. + pathPrefixSet = new PathPrefixSet(); + pathPrefixSet.add(JUNIT_PREFIX); + pathPrefixSet.add(THIS_CLASS_PREFIX); + resourceOracle.setPathPrefixes(pathPrefixSet); + resourceOracle.refresh(logger); + assertSame(resourceMap, resourceOracle.getResourceMap()); + + // Setting identical result should have no effect. + pathPrefixSet.add(JUNIT_PREFIX_DUP); + resourceOracle.refresh(logger); + assertSame(resourceMap, resourceOracle.getResourceMap()); + + // Actually change the working set. + pathPrefixSet.add(THIS_CLASS_PREFIX_PLUS); + resourceOracle.refresh(logger); + Map<String, Resource> newResourceMap = resourceOracle.getResourceMap(); + assertNotSame(resourceMap, newResourceMap); + assertEquals(3, newResourceMap.size()); + } +}
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/ResourceOracleImplTest.java b/dev/core/test/com/google/gwt/dev/resource/impl/ResourceOracleImplTest.java new file mode 100644 index 0000000..c6fe7e0 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/resource/impl/ResourceOracleImplTest.java
@@ -0,0 +1,491 @@ +/* + * Copyright 2008 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.resource.impl; + +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.dev.resource.Resource; +import com.google.gwt.util.tools.Utility; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Tests {@link ResourceOracleImpl}. + * + * <pre> + * Important states to test: + * - No class path entries + * - A single class path entries + * - init/add/update/remove + * - Multiple class path entries + * - init/add/update/remove + * - same as previous with shadowing + * - same as previous with superceding + * </pre> + */ +public class ResourceOracleImplTest extends AbstractResourceOrientedTestBase { + + // Starts empty but can change during tests. + private static class MOCK_CPE0 extends MockClassPathEntry { + public MOCK_CPE0() { + super("/cpe0/"); + } + } + + private static class MOCK_CPE3 extends MockClassPathEntry { + public MOCK_CPE3() { + super("/cpe3/"); + } + } + + private static class ResourceOracleSnapshot { + private final Set<Resource> resources; + private final Map<String, Resource> resourceMap; + private final Set<String> pathNames; + + public ResourceOracleSnapshot(ResourceOracleImpl oracle) { + resources = oracle.getResources(); + resourceMap = oracle.getResourceMap(); + pathNames = oracle.getPathNames(); + } + + public void assertCollectionsConsistent(int expectedSize) { + assertEquals(expectedSize, resources.size()); + assertEquals(resources.size(), resourceMap.size()); + assertEquals(resources.size(), pathNames.size()); + + // Ensure every resource is in the map correctly. + for (Resource r : resources) { + assertSame(r, resourceMap.get(r.getPath())); + } + + // Ensure that every resource path is in the set. + for (Resource r : resources) { + assertTrue(pathNames.contains(r.getPath())); + } + } + + public void assertNotSameCollections(ResourceOracleSnapshot other) { + assertNotSame(resourceMap, other.resourceMap); + assertNotSame(resources, other.resources); + assertNotSame(pathNames, other.pathNames); + } + + public void assertPathIncluded(String path) { + assertNotNull(findResourceWithPath(path)); + } + + /** + * Asserts that a resource having the specified path is present and that it + * was contributed by the specified classpath entry. + */ + public void assertPathIncluded(String expectedPath, + ClassPathEntry expectedCpe) { + AbstractResource r = findResourceWithPath(expectedPath); + assertNotNull(r); + ClassPathEntry actualCpe = r.getClassPathEntry(); + assertEquals(expectedCpe.getLocation(), actualCpe.getLocation()); + } + + public void assertPathNotIncluded(String path) { + assertNull(findResourceWithPath(path)); + } + + public void assertSameCollections(ResourceOracleSnapshot other) { + assertSame(resourceMap, other.resourceMap); + assertSame(resources, other.resources); + assertSame(pathNames, other.pathNames); + } + + public AbstractResource findResourceWithPath(String path) { + for (Resource r : resources) { + if (r.getPath().equals(path)) { + return (AbstractResource) r; + } + } + return null; + } + } + + public void testNoClassPathEntries() { + TreeLogger logger = createTestTreeLogger(); + ResourceOracleImpl oracle = createResourceOracle(new MOCK_CPE0()); + ResourceOracleSnapshot s = refreshAndSnapshot(logger, oracle); + s.assertCollectionsConsistent(0); + } + + /** + * Tests the actual reading of resources. + * + * @throws URISyntaxException + * @throws IOException + * @throws UnableToCompleteException + */ + public void testReadingResource() throws IOException, URISyntaxException { + ClassPathEntry cpe1jar = getClassPathEntry1AsJar(); + ClassPathEntry cpe1dir = getClassPathEntry1AsDirectory(); + + ClassPathEntry cpe2jar = getClassPathEntry2AsJar(); + ClassPathEntry cpe2dir = getClassPathEntry2AsDirectory(); + + testReadingResource(cpe1jar, cpe2jar); + testReadingResource(cpe1dir, cpe2jar); + + testReadingResource(cpe1jar, cpe2dir); + testReadingResource(cpe1dir, cpe2dir); + } + + public void testResourceAddition() throws IOException, URISyntaxException { + ClassPathEntry cpe1mock = getClassPathEntry1AsMock(); + ClassPathEntry cpe1jar = getClassPathEntry1AsJar(); + ClassPathEntry cpe1dir = getClassPathEntry1AsDirectory(); + + ClassPathEntry cpe2mock = getClassPathEntry2AsMock(); + ClassPathEntry cpe2jar = getClassPathEntry2AsJar(); + ClassPathEntry cpe2dir = getClassPathEntry2AsDirectory(); + + testResourceAddition(cpe1jar, cpe2jar); + testResourceAddition(cpe1dir, cpe2jar); + testResourceAddition(cpe1mock, cpe2jar); + + testResourceAddition(cpe1jar, cpe2dir); + testResourceAddition(cpe1dir, cpe2dir); + testResourceAddition(cpe1mock, cpe2dir); + + testResourceAddition(cpe1jar, cpe2mock); + testResourceAddition(cpe1dir, cpe2mock); + testResourceAddition(cpe1mock, cpe2mock); + } + + public void testResourceDeletion() throws IOException, URISyntaxException { + ClassPathEntry cpe1mock = getClassPathEntry1AsMock(); + ClassPathEntry cpe1jar = getClassPathEntry1AsJar(); + ClassPathEntry cpe1dir = getClassPathEntry1AsDirectory(); + + ClassPathEntry cpe2mock = getClassPathEntry2AsMock(); + ClassPathEntry cpe2jar = getClassPathEntry2AsJar(); + ClassPathEntry cpe2dir = getClassPathEntry2AsDirectory(); + + testResourceDeletion(cpe1jar, cpe2jar); + testResourceDeletion(cpe1dir, cpe2jar); + testResourceDeletion(cpe1mock, cpe2jar); + + testResourceDeletion(cpe1jar, cpe2dir); + testResourceDeletion(cpe1dir, cpe2dir); + testResourceDeletion(cpe1mock, cpe2dir); + + testResourceDeletion(cpe1jar, cpe2mock); + testResourceDeletion(cpe1dir, cpe2mock); + testResourceDeletion(cpe1mock, cpe2mock); + } + + public void testResourceModification() throws IOException, URISyntaxException { + ClassPathEntry cpe1mock = getClassPathEntry1AsMock(); + ClassPathEntry cpe1jar = getClassPathEntry1AsJar(); + ClassPathEntry cpe1dir = getClassPathEntry1AsDirectory(); + + ClassPathEntry cpe2mock = getClassPathEntry2AsMock(); + ClassPathEntry cpe2jar = getClassPathEntry2AsJar(); + ClassPathEntry cpe2dir = getClassPathEntry2AsDirectory(); + + testResourceModification(cpe1jar, cpe2jar); + testResourceModification(cpe1dir, cpe2jar); + testResourceModification(cpe1mock, cpe2jar); + + testResourceModification(cpe1jar, cpe2dir); + testResourceModification(cpe1dir, cpe2dir); + testResourceModification(cpe1mock, cpe2dir); + + testResourceModification(cpe1jar, cpe2mock); + testResourceModification(cpe1dir, cpe2mock); + testResourceModification(cpe1mock, cpe2mock); + + /* + * TODO(bruce): figure out a good way to test real resource modifications of + * jar files and directories + */ + } + + /** + * Creates an array of class path entries, setting up each one with a + * well-known set of client prefixes. + * + * @param entries + * @return + */ + private ResourceOracleImpl createResourceOracle(ClassPathEntry... entries) { + PathPrefixSet pps = new PathPrefixSet(); + pps.add(new PathPrefix("com/google/gwt/user/client/", null)); + pps.add(new PathPrefix("org/example/bar/client/", null)); + pps.add(new PathPrefix("org/example/foo/client/", null)); + pps.add(new PathPrefix("com/google/gwt/i18n/client/", null)); + + List<ClassPathEntry> classPath = new ArrayList<ClassPathEntry>(); + for (ClassPathEntry entry : entries) { + classPath.add(entry); + } + ResourceOracleImpl oracle = new ResourceOracleImpl(classPath); + oracle.setPathPrefixes(pps); + return oracle; + } + + private ResourceOracleSnapshot refreshAndSnapshot(TreeLogger logger, + ResourceOracleImpl oracle) { + oracle.refresh(logger); + return new ResourceOracleSnapshot(oracle); + } + + private void testReadingResource(ClassPathEntry cpe1, ClassPathEntry cpe2) + throws IOException { + TreeLogger logger = createTestTreeLogger(); + + ResourceOracleImpl oracle = createResourceOracle(cpe1, cpe2); + + oracle.refresh(logger); + ResourceOracleSnapshot s = new ResourceOracleSnapshot(oracle); + s.assertCollectionsConsistent(9); + s.assertPathIncluded("com/google/gwt/user/client/Command.java", cpe1); + s.assertPathIncluded("com/google/gwt/i18n/client/Messages.java", cpe2); + + { + /* + * Read a resource in cpe1. + */ + AbstractResource res = s.findResourceWithPath("com/google/gwt/user/client/Command.java"); + BufferedReader rdr = null; + try { + InputStream is = res.openContents(); + assertNotNull(is); + rdr = new BufferedReader(new InputStreamReader(is)); + assertTrue(rdr.readLine().indexOf( + "package com.google.gwt.dev.resource.impl.testdata.cpe1.com.google.gwt.user.client;") >= 0); + } finally { + Utility.close(rdr); + } + } + + { + /* + * Read a resource in cpe2. + */ + AbstractResource res = s.findResourceWithPath("com/google/gwt/i18n/client/Messages.java"); + BufferedReader rdr = null; + try { + InputStream is = res.openContents(); + assertNotNull(is); + rdr = new BufferedReader(new InputStreamReader(is)); + assertTrue(rdr.readLine().indexOf( + "package com.google.gwt.dev.resource.impl.testdata.cpe2.com.google.gwt.i18n.client;") >= 0); + } finally { + Utility.close(rdr); + } + } + + { + /* + * TODO: Try to read an invalid resource and watch it fail as intended. + */ + } + } + + private void testResourceAddition(ClassPathEntry cpe1, ClassPathEntry cpe2) { + TreeLogger logger = createTestTreeLogger(); + + MOCK_CPE0 cpe0 = new MOCK_CPE0(); + MOCK_CPE3 cpe3 = new MOCK_CPE3(); + ResourceOracleImpl oracle = createResourceOracle(cpe0, cpe1, cpe2, cpe3); + + { + /* + * Ensure it's correct as a baseline. These tests have hard-coded + * assumptions about the contents of each classpath entry. + */ + ResourceOracleSnapshot s = refreshAndSnapshot(logger, oracle); + s.assertCollectionsConsistent(9); + s.assertPathIncluded("com/google/gwt/user/client/Command.java"); + s.assertPathIncluded("com/google/gwt/user/client/Timer.java"); + s.assertPathIncluded("com/google/gwt/user/client/ui/Widget.java"); + s.assertPathIncluded("org/example/bar/client/BarClient1.txt"); + s.assertPathIncluded("org/example/bar/client/BarClient2.txt"); + s.assertPathIncluded("org/example/bar/client/BarClient3.txt"); + s.assertPathIncluded("org/example/bar/client/etc/BarEtc.txt"); + s.assertPathIncluded("org/example/foo/client/FooClient.java"); + s.assertPathIncluded("com/google/gwt/i18n/client/Messages.java"); + } + + { + /* + * Add duplicate resources later in the classpath, which won't be found. + * Consequently, the collections' identities should not change. + */ + cpe3.addResource("com/google/gwt/user/client/Command.java"); + cpe3.addResource("com/google/gwt/user/client/Timer.java"); + cpe3.addResource("com/google/gwt/bar/client/etc/BarEtc.txt"); + + ResourceOracleSnapshot before = new ResourceOracleSnapshot(oracle); + ResourceOracleSnapshot after = refreshAndSnapshot(logger, oracle); + after.assertCollectionsConsistent(9); + after.assertSameCollections(before); + } + + { + /* + * Add a unique resource later in the classpath, which will be found. + * Consequently, the collections' identities should change. + */ + cpe3.addResource("com/google/gwt/i18n/client/Constants.java"); + + ResourceOracleSnapshot before = new ResourceOracleSnapshot(oracle); + ResourceOracleSnapshot after = refreshAndSnapshot(logger, oracle); + after.assertCollectionsConsistent(10); + after.assertNotSameCollections(before); + after.assertPathIncluded("com/google/gwt/i18n/client/Constants.java"); + } + } + + private void testResourceDeletion(ClassPathEntry cpe1, ClassPathEntry cpe2) { + TreeLogger logger = createTestTreeLogger(); + + MOCK_CPE0 cpe0 = new MOCK_CPE0(); + MOCK_CPE3 cpe3 = new MOCK_CPE3(); + ResourceOracleImpl oracle = createResourceOracle(cpe0, cpe1, cpe2, cpe3); + + /* + * Intentionally add some duplicate resources. + */ + cpe0.addResource("com/google/gwt/user/client/Command.java"); + cpe0.addResource("com/google/gwt/i18n/client/Constants.java"); + cpe3.addResource("com/google/gwt/user/client/Command.java"); + cpe3.addResource("com/google/gwt/i18n/client/Constants.java"); + + { + /* + * Ensure it's correct as a baseline. These tests have hard-coded + * assumptions about the contents of each classpath entry. + */ + ResourceOracleSnapshot s = refreshAndSnapshot(logger, oracle); + s.assertCollectionsConsistent(10); + s.assertPathIncluded("com/google/gwt/user/client/Command.java"); + s.assertPathIncluded("com/google/gwt/user/client/Timer.java"); + s.assertPathIncluded("com/google/gwt/user/client/ui/Widget.java"); + s.assertPathIncluded("org/example/bar/client/BarClient1.txt"); + s.assertPathIncluded("org/example/bar/client/BarClient2.txt"); + s.assertPathIncluded("org/example/bar/client/BarClient3.txt"); + s.assertPathIncluded("org/example/bar/client/etc/BarEtc.txt"); + s.assertPathIncluded("org/example/foo/client/FooClient.java"); + s.assertPathIncluded("com/google/gwt/i18n/client/Messages.java"); + s.assertPathIncluded("com/google/gwt/i18n/client/Constants.java"); + } + + { + /* + * Remove a shadowed resource, which shouldn't have been found anyway. + * Consequently, the collections' identities should not change. + */ + cpe3.removeResource("com/google/gwt/user/client/Command.java"); + cpe3.removeResource("com/google/gwt/i18n/client/Constants.java"); + + ResourceOracleSnapshot before = new ResourceOracleSnapshot(oracle); + ResourceOracleSnapshot after = refreshAndSnapshot(logger, oracle); + after.assertCollectionsConsistent(10); + after.assertSameCollections(before); + } + + { + /* + * Remove a unique resource, which will no longer be found. We also add a + * new one to ensure that lack of size change doesn't confuse anything. + * Consequently, the collections' identities should change. + */ + cpe0.removeResource("com/google/gwt/i18n/client/Constants.java"); + cpe3.addResource("com/google/gwt/user/client/Window.java"); + + ResourceOracleSnapshot before = new ResourceOracleSnapshot(oracle); + ResourceOracleSnapshot after = refreshAndSnapshot(logger, oracle); + after.assertCollectionsConsistent(10); + after.assertNotSameCollections(before); + after.assertPathIncluded("com/google/gwt/user/client/Window.java"); + after.assertPathNotIncluded("com/google/gwt/i18n/client/Constants.java"); + } + } + + private void testResourceModification(ClassPathEntry cpe1, ClassPathEntry cpe2) { + TreeLogger logger = createTestTreeLogger(); + + MOCK_CPE0 cpe0 = new MOCK_CPE0(); + MOCK_CPE3 cpe3 = new MOCK_CPE3(); + ResourceOracleImpl oracle = createResourceOracle(cpe0, cpe1, cpe2, cpe3); + + { + /* + * Baseline assumptions about the set of resources present by default. + */ + oracle.refresh(logger); + ResourceOracleSnapshot s = new ResourceOracleSnapshot(oracle); + s.assertPathIncluded("com/google/gwt/user/client/Command.java", cpe1); + s.assertPathIncluded("com/google/gwt/user/client/Timer.java", cpe1); + } + + // Add intentionally duplicate resources. + cpe0.addResource("com/google/gwt/user/client/Command.java"); + cpe3.addResource("com/google/gwt/user/client/Timer.java"); + + { + /* + * Ensure that the dups have the effect we expect. + */ + oracle.refresh(logger); + ResourceOracleSnapshot s = new ResourceOracleSnapshot(oracle); + s.assertPathIncluded("com/google/gwt/user/client/Command.java", cpe0); + s.assertPathIncluded("com/google/gwt/user/client/Timer.java", cpe1); + } + + { + /* + * Change a mock resource that was shadowed to ensure that the change + * isn't observed. + */ + ResourceOracleSnapshot before = new ResourceOracleSnapshot(oracle); + before.assertCollectionsConsistent(9); + + cpe3.updateResource("com/google/gwt/user/client/Timer.java"); + + ResourceOracleSnapshot after = refreshAndSnapshot(logger, oracle); + after.assertSameCollections(before); + } + + { + /* + * Change a mock resource that was not shadowed to ensure that the change + * is observed. + */ + ResourceOracleSnapshot before = new ResourceOracleSnapshot(oracle); + before.assertCollectionsConsistent(9); + + cpe0.updateResource("com/google/gwt/user/client/Command.java"); + + ResourceOracleSnapshot after = refreshAndSnapshot(logger, oracle); + after.assertNotSameCollections(before); + } + } +}
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1.jar b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1.jar new file mode 100644 index 0000000..5045c12 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1.jar Binary files differ
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/com/google/gwt/user/User.gwt.xml b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/com/google/gwt/user/User.gwt.xml new file mode 100644 index 0000000..bdf08de --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/com/google/gwt/user/User.gwt.xml
@@ -0,0 +1 @@ +test file \ No newline at end of file
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/com/google/gwt/user/client/Command.java b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/com/google/gwt/user/client/Command.java new file mode 100644 index 0000000..42ba14b --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/com/google/gwt/user/client/Command.java
@@ -0,0 +1,5 @@ +package com.google.gwt.dev.resource.impl.testdata.cpe1.com.google.gwt.user.client; + +public class Command { + // test class +} \ No newline at end of file
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/com/google/gwt/user/client/Timer.java b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/com/google/gwt/user/client/Timer.java new file mode 100644 index 0000000..d845e45 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/com/google/gwt/user/client/Timer.java
@@ -0,0 +1,5 @@ +package com.google.gwt.dev.resource.impl.testdata.cpe1.com.google.gwt.user.client; + +public class Timer { + // test class +} \ No newline at end of file
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/com/google/gwt/user/client/ui/Widget.java b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/com/google/gwt/user/client/ui/Widget.java new file mode 100644 index 0000000..79acdc7 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/com/google/gwt/user/client/ui/Widget.java
@@ -0,0 +1,5 @@ +package com.google.gwt.dev.resource.impl.testdata.cpe1.com.google.gwt.user.client.ui; + +public class Widget { + // test class +} \ No newline at end of file
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/org/example/bar/client/BarClient1.txt b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/org/example/bar/client/BarClient1.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/org/example/bar/client/BarClient1.txt
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/org/example/bar/client/BarClient2.txt b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/org/example/bar/client/BarClient2.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/org/example/bar/client/BarClient2.txt
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/org/example/bar/client/etc/BarEtc.txt b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/org/example/bar/client/etc/BarEtc.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/org/example/bar/client/etc/BarEtc.txt
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/org/example/foo/client/FooClient.java b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/org/example/foo/client/FooClient.java new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/org/example/foo/client/FooClient.java
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/org/example/foo/server/FooServer.java b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/org/example/foo/server/FooServer.java new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe1/org/example/foo/server/FooServer.java
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe2.jar b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe2.jar new file mode 100644 index 0000000..08761bb --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe2.jar Binary files differ
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe2/com/google/gwt/i18n/I18N.gwt.xml b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe2/com/google/gwt/i18n/I18N.gwt.xml new file mode 100644 index 0000000..bdf08de --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe2/com/google/gwt/i18n/I18N.gwt.xml
@@ -0,0 +1 @@ +test file \ No newline at end of file
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe2/com/google/gwt/i18n/client/Messages.java b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe2/com/google/gwt/i18n/client/Messages.java new file mode 100644 index 0000000..dff41a8 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe2/com/google/gwt/i18n/client/Messages.java
@@ -0,0 +1,5 @@ +package com.google.gwt.dev.resource.impl.testdata.cpe2.com.google.gwt.i18n.client; + +public class Messages { + // test class +} \ No newline at end of file
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe2/com/google/gwt/i18n/rebind/LocalizableGenerator.java b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe2/com/google/gwt/i18n/rebind/LocalizableGenerator.java new file mode 100644 index 0000000..dae8c55 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe2/com/google/gwt/i18n/rebind/LocalizableGenerator.java
@@ -0,0 +1,5 @@ +package com.google.gwt.dev.resource.impl.testdata.cpe2.com.google.gwt.i18n.rebind; + +public class LocalizableGenerator { + // test class +}
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe2/org/example/bar/client/BarClient2.txt b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe2/org/example/bar/client/BarClient2.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe2/org/example/bar/client/BarClient2.txt
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe2/org/example/bar/client/BarClient3.txt b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe2/org/example/bar/client/BarClient3.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/cpe2/org/example/bar/client/BarClient3.txt
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/testdata/rebuild_jars b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/rebuild_jars new file mode 100644 index 0000000..630dc1a --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/resource/impl/testdata/rebuild_jars
@@ -0,0 +1,7 @@ +cd cpe1 +find . -type f | grep -v "\.svn" | xargs jar cvf ../cpe1.jar +cd .. +cd cpe2 +find . -type f | grep -v "\.svn" | xargs jar cvf ../cpe2.jar +cd .. +
diff --git a/dev/core/test/com/google/gwt/dev/shell/StandardGeneratorContextTest.java b/dev/core/test/com/google/gwt/dev/shell/StandardGeneratorContextTest.java index 4b556f4..7b06e39 100644 --- a/dev/core/test/com/google/gwt/dev/shell/StandardGeneratorContextTest.java +++ b/dev/core/test/com/google/gwt/dev/shell/StandardGeneratorContextTest.java
@@ -1,5 +1,5 @@ /* - * Copyright 2008 Google Inc. + * Copyright 2007 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 @@ -18,59 +18,67 @@ import com.google.gwt.core.ext.BadPropertyValueException; import com.google.gwt.core.ext.Generator; import com.google.gwt.core.ext.GeneratorContext; -import com.google.gwt.core.ext.Linker; import com.google.gwt.core.ext.PropertyOracle; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.linker.Artifact; import com.google.gwt.core.ext.linker.ArtifactSet; import com.google.gwt.core.ext.linker.GeneratedResource; -import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.dev.cfg.PublicOracle; -import com.google.gwt.dev.jdt.CacheManager; +import com.google.gwt.dev.javac.CompilationState; +import com.google.gwt.dev.javac.JavaSourceFile; +import com.google.gwt.dev.javac.JavaSourceOracle; +import com.google.gwt.dev.resource.Resource; import com.google.gwt.dev.util.Util; import junit.framework.TestCase; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; -import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Set; /** * A wide variety of tests on {@link StandardGeneratorContext}. */ public class StandardGeneratorContextTest extends TestCase { - private static class MockArtifact extends Artifact<MockArtifact> { + public static class MockCompilationState extends CompilationState { - public MockArtifact() { - super(Linker.class); + public MockCompilationState() { + super(new JavaSourceOracle() { + public Set<String> getClassNames() { + return Collections.emptySet(); + } + + public Set<JavaSourceFile> getSourceFiles() { + return Collections.emptySet(); + } + + public Map<String, JavaSourceFile> getSourceMap() { + return Collections.emptyMap(); + } + }); } - @Override - protected int compareToComparableArtifact(MockArtifact o) { - return 0; - } - - @Override - protected Class<MockArtifact> getComparableArtifactType() { - return MockArtifact.class; - } - - @Override - public int hashCode() { - return 0; - } } - private static class MockCacheManager extends CacheManager { + private static class MockGenerator extends Generator { + @Override + public String generate(TreeLogger logger, GeneratorContext context, + String typeName) throws UnableToCompleteException { + return typeName; + } } private static class MockPropertyOracle implements PropertyOracle { @@ -85,23 +93,32 @@ } } - private static class MockGenerator extends Generator { - @Override - public String generate(TreeLogger logger, GeneratorContext context, - String typeName) throws UnableToCompleteException { - return typeName; - } - } - private static class MockPublicOracle implements PublicOracle { - public URL findPublicFile(String partialPath) { + public Resource findPublicFile(String partialPath) { if ("onPublicPath.txt".equals(partialPath)) { - try { - return new File("").toURL(); - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } + return new Resource() { + + @Override + public String getLocation() { + return "/mock/onPublicPath.txt"; + } + + @Override + public String getPath() { + return "onPublicPath.txt"; + } + + @Override + public URL getURL() { + return null; + } + + @Override + public InputStream openContents() { + return new ByteArrayInputStream(Util.getBytes("w00t!")); + } + }; } return null; } @@ -112,31 +129,27 @@ } - private static class MockTypeOracle extends TypeOracle { - } - private final ArtifactSet artifactSet = new ArtifactSet(); + private final StandardGeneratorContext genCtx; + private final CompilationState mockCompilationState = new MockCompilationState(); + private final TreeLogger mockLogger = TreeLogger.NULL; + private final PropertyOracle mockPropOracle = new MockPropertyOracle(); + private final PublicOracle mockPublicOracle = new MockPublicOracle(); + private int tempFileCounter; + private final File tempGenDir; + private final File tempOutDir; /** * Stores the File objects to delete in the order they were created. Delete * them in reverse order. */ private final List<File> toDelete = new ArrayList<File>(); - private final TypeOracle mockTypeOracle = new MockTypeOracle(); - private final PropertyOracle mockPropOracle = new MockPropertyOracle(); - private final PublicOracle mockPublicOracle = new MockPublicOracle(); - private final File tempGenDir; - private final File tempOutDir; - private final CacheManager mockCacheManager = new MockCacheManager(); - private final TreeLogger mockLogger = TreeLogger.NULL; - private final StandardGeneratorContext genCtx; - private int tempFileCounter; public StandardGeneratorContextTest() { tempGenDir = createTempDir("gwt-gen-"); tempOutDir = createTempDir("gwt-out-"); - genCtx = new StandardGeneratorContext(mockTypeOracle, mockPropOracle, - mockPublicOracle, tempGenDir, tempOutDir, mockCacheManager, artifactSet); + genCtx = new StandardGeneratorContext(mockCompilationState, mockPropOracle, + mockPublicOracle, tempGenDir, tempOutDir, artifactSet); } public void testTryCreateResource_badFileName() { @@ -267,15 +280,6 @@ genCtx.finish(mockLogger); } - public void testTryCreateResource_duplicateCreationAttempt() - throws UnableToCompleteException { - String path = createTempOutFilename(); - OutputStream os1 = genCtx.tryCreateResource(mockLogger, path); - assertNotNull(os1); - OutputStream os2 = genCtx.tryCreateResource(mockLogger, path); - assertNull(os2); - } - public void testTryCreateResource_duplicateCreationAfterCommit() throws UnableToCompleteException, UnsupportedEncodingException, IOException { @@ -291,6 +295,15 @@ assertNull(os2); } + public void testTryCreateResource_duplicateCreationAttempt() + throws UnableToCompleteException { + String path = createTempOutFilename(); + OutputStream os1 = genCtx.tryCreateResource(mockLogger, path); + assertNotNull(os1); + OutputStream os2 = genCtx.tryCreateResource(mockLogger, path); + assertNull(os2); + } + public void testTryCreateResource_finishCalledTwice() throws UnableToCompleteException, IOException { // Borrow impl. @@ -334,12 +347,6 @@ } @Override - protected void setUp() throws Exception { - mockCacheManager.invalidateVolatileFiles(); - artifactSet.clear(); - } - - @Override protected void tearDown() throws Exception { for (int i = toDelete.size() - 1; i >= 0; --i) { File f = toDelete.get(i);
diff --git a/dev/core/test/com/google/gwt/dev/typeinfo/test/InteractiveTypeOracle.java b/dev/core/test/com/google/gwt/dev/typeinfo/test/InteractiveTypeOracle.java index 66047b2..f20de04 100644 --- a/dev/core/test/com/google/gwt/dev/typeinfo/test/InteractiveTypeOracle.java +++ b/dev/core/test/com/google/gwt/dev/typeinfo/test/InteractiveTypeOracle.java
@@ -27,7 +27,7 @@ import com.google.gwt.core.ext.typeinfo.ParseException; import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.core.ext.typeinfo.TypeOracleException; -import com.google.gwt.dev.jdt.TypeOracleBuilder; +import com.google.gwt.dev.javac.TypeOracleMediator; import com.google.gwt.dev.util.log.AbstractTreeLogger; import com.google.gwt.dev.util.log.PrintWriterTreeLogger; @@ -81,9 +81,9 @@ // Build an oracle. // - TypeOracleBuilder builder = new TypeOracleBuilder(); + TypeOracleMediator mediator = new TypeOracleMediator(); + TypeOracle oracle = mediator.getTypeOracle(); // TODO: add compilation units - TypeOracle oracle = builder.build(logger); // Create an interactive wrapper around the oracle. //
diff --git a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiContainer.java b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiContainer.java index c7d1c20..2a89cb9 100644 --- a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiContainer.java +++ b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiContainer.java
@@ -18,13 +18,13 @@ import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider; import com.google.gwt.core.ext.typeinfo.JPackage; import com.google.gwt.core.ext.typeinfo.NotFoundException; import com.google.gwt.core.ext.typeinfo.TypeOracle; -import com.google.gwt.dev.jdt.CacheManager; -import com.google.gwt.dev.jdt.TypeOracleBuilder; -import com.google.gwt.dev.jdt.URLCompilationUnitProvider; +import com.google.gwt.dev.javac.CompilationUnit; +import com.google.gwt.dev.javac.JdtCompiler; +import com.google.gwt.dev.javac.TypeOracleMediator; +import com.google.gwt.dev.javac.impl.FileCompilationUnit; import java.io.BufferedReader; import java.io.File; @@ -33,13 +33,13 @@ import java.io.FileReader; import java.io.IOException; import java.net.MalformedURLException; -import java.net.URL; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Properties; +import java.util.Set; import java.util.Vector; /** @@ -47,6 +47,7 @@ * */ public class ApiContainer { + private HashMap<String, ApiPackage> apiPackages = new HashMap<String, ApiPackage>(); private HashMap<String, String> excludedFiles = null; private TreeLogger logger = null; @@ -119,8 +120,8 @@ public TreeLogger getLogger() { return logger; } - - private void addCompilationUnitsInPath(TypeOracleBuilder builder, + + private void addCompilationUnitsInPath(Set<CompilationUnit> units, File sourcePathEntry) throws NotFoundException, IOException, UnableToCompleteException { File[] files = sourcePathEntry.listFiles(); @@ -130,7 +131,7 @@ } for (int i = 0; i < files.length; i++) { - File file = files[i]; + final File file = files[i]; // Ignore files like .svn and .cvs if (file.getName().startsWith(".") || file.getName().equals("CVS")) { continue; @@ -150,19 +151,15 @@ } if (isValidPackage(pkgName, sourcePathEntry.toURL().toString())) { // Add if it's a source file and the package and fileNames are okay - URL location = file.toURL(); - CompilationUnitProvider cup = new URLCompilationUnitProvider( - location, pkgName); - logger.log(TreeLogger.DEBUG, "+ to CompilationUnit" + ", location=" - + location + ", pkgName=" + pkgName, null); - builder.addCompilationUnit(cup); + CompilationUnit unit = new FileCompilationUnit(file, pkgName); + units.add(unit); numFilesCount++; } else { logger.log(TreeLogger.SPAM, " not adding file " + file.toURL(), null); } } else { // Recurse into subDirs - addCompilationUnitsInPath(builder, file); + addCompilationUnitsInPath(units, file); } } } @@ -171,12 +168,14 @@ IOException, UnableToCompleteException { numFilesCount = 0; - TypeOracleBuilder builder = new TypeOracleBuilder(new CacheManager(null, - null, ApiCompatibilityChecker.DISABLE_CHECKS)); + TypeOracleMediator mediator = new TypeOracleMediator(); + Set<CompilationUnit> units = new HashSet<CompilationUnit>(); for (Iterator<File> i = sourceTrees.iterator(); i.hasNext();) { - addCompilationUnitsInPath(builder, i.next()); + addCompilationUnitsInPath(units, i.next()); } - typeOracle = builder.build(logger); + JdtCompiler.compile(units); + mediator.refresh(logger, units); + typeOracle = mediator.getTypeOracle(); logger.log(TreeLogger.INFO, "API " + name + ", Finished with building typeOracle, added " + numFilesCount + " files", null);
diff --git a/tools/api-checker/test/com/google/gwt/tools/apichecker/ApiCompatibilityTest.java b/tools/api-checker/test/com/google/gwt/tools/apichecker/ApiCompatibilityTest.java index f6fdbd9..b643388 100644 --- a/tools/api-checker/test/com/google/gwt/tools/apichecker/ApiCompatibilityTest.java +++ b/tools/api-checker/test/com/google/gwt/tools/apichecker/ApiCompatibilityTest.java
@@ -19,15 +19,16 @@ import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.NotFoundException; import com.google.gwt.core.ext.typeinfo.TypeOracle; -import com.google.gwt.dev.jdt.CacheManager; -import com.google.gwt.dev.jdt.TypeOracleBuilder; +import com.google.gwt.dev.javac.CompilationUnit; +import com.google.gwt.dev.javac.JdtCompiler; +import com.google.gwt.dev.javac.TypeOracleMediator; import com.google.gwt.dev.util.log.AbstractTreeLogger; import com.google.gwt.dev.util.log.PrintWriterTreeLogger; -import com.google.gwt.dev.jdt.StaticCompilationUnitProvider; import junit.framework.TestCase; import java.util.HashSet; +import java.util.Set; /** * @@ -40,18 +41,49 @@ * test white-list support. */ public class ApiCompatibilityTest extends TestCase { - // These cups are slightly different from the cups in ApiContainerTest - static StaticCompilationUnitProvider cuApiClass = new StaticCompilationUnitProvider( - "test.apicontainer", "ApiClass", getSourceForApiClass()); - static StaticCompilationUnitProvider cuNonApiClass = new StaticCompilationUnitProvider( - "test.apicontainer", "NonApiClass", getSourceForNonApiClass()); - static StaticCompilationUnitProvider cuNonApiPackage = new StaticCompilationUnitProvider( - "test.nonapipackage", "TestClass", getSourceForTestClass()); - static StaticCompilationUnitProvider cuObject = new StaticCompilationUnitProvider( - "java.lang", "Object", getSourceForObject()); - static StaticCompilationUnitProvider cuThrowable = new StaticCompilationUnitProvider( - "java.lang", "Throwable", getSourceForThrowable()); + static class StaticCompilationUnit extends CompilationUnit { + + private final char[] source; + private final String typeName; + + public StaticCompilationUnit(String typeName, char[] source) { + this.typeName = typeName; + this.source = source; + } + + @Override + public String getDisplayLocation() { + return "/mock/" + typeName; + } + + @Override + public String getSource() { + return String.valueOf(source); + } + + @Override + public String getTypeName() { + return typeName; + } + + @Override + public boolean isGenerated() { + return false; + } + } + + // These cups are slightly different from the cups in ApiContainerTest + static StaticCompilationUnit cuApiClass = new StaticCompilationUnit( + "test.apicontainer.ApiClass", getSourceForApiClass()); + static StaticCompilationUnit cuNonApiClass = new StaticCompilationUnit( + "test.apicontainer.NonApiClass", getSourceForNonApiClass()); + static StaticCompilationUnit cuNonApiPackage = new StaticCompilationUnit( + "test.nonapipackage.TestClass", getSourceForTestClass()); + static StaticCompilationUnit cuObject = new StaticCompilationUnit( + "java.lang.Object", getSourceForObject()); + static StaticCompilationUnit cuThrowable = new StaticCompilationUnit( + "java.lang.Throwable", getSourceForThrowable()); private static char[] getSourceForApiClass() { StringBuffer sb = new StringBuffer(); @@ -115,14 +147,16 @@ public TypeOracle getNewTypeOracleWithCompilationUnitsAdded( AbstractTreeLogger logger) throws UnableToCompleteException { - TypeOracleBuilder builder1 = new TypeOracleBuilder(new CacheManager(null, - null, ApiCompatibilityChecker.DISABLE_CHECKS)); - builder1.addCompilationUnit(cuObject); - builder1.addCompilationUnit(cuNonApiClass); - builder1.addCompilationUnit(cuApiClass); - builder1.addCompilationUnit(cuNonApiPackage); - builder1.addCompilationUnit(cuThrowable); - return builder1.build(logger); + TypeOracleMediator mediator = new TypeOracleMediator(); + Set<CompilationUnit> units = new HashSet<CompilationUnit>(); + units.add(cuObject); + units.add(cuNonApiClass); + units.add(cuApiClass); + units.add(cuNonApiPackage); + units.add(cuThrowable); + JdtCompiler.compile(units); + mediator.refresh(logger, units); + return mediator.getTypeOracle(); } @Override
diff --git a/tools/api-checker/test/com/google/gwt/tools/apichecker/ApiContainerTest.java b/tools/api-checker/test/com/google/gwt/tools/apichecker/ApiContainerTest.java index d3dc300..a390b08 100644 --- a/tools/api-checker/test/com/google/gwt/tools/apichecker/ApiContainerTest.java +++ b/tools/api-checker/test/com/google/gwt/tools/apichecker/ApiContainerTest.java
@@ -19,14 +19,18 @@ import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.JAbstractMethod; import com.google.gwt.core.ext.typeinfo.TypeOracle; -import com.google.gwt.dev.jdt.CacheManager; -import com.google.gwt.dev.jdt.StaticCompilationUnitProvider; -import com.google.gwt.dev.jdt.TypeOracleBuilder; +import com.google.gwt.dev.javac.CompilationUnit; +import com.google.gwt.dev.javac.JdtCompiler; +import com.google.gwt.dev.javac.TypeOracleMediator; import com.google.gwt.dev.util.log.AbstractTreeLogger; import com.google.gwt.dev.util.log.PrintWriterTreeLogger; +import com.google.gwt.tools.apichecker.ApiCompatibilityTest.StaticCompilationUnit; import junit.framework.TestCase; +import java.util.HashSet; +import java.util.Set; + /** * Test ApiContainer. */ @@ -61,19 +65,16 @@ } } - static StaticCompilationUnitProvider cuApiClass = new StaticCompilationUnitProvider( - "test.apicontainer", "ApiClass", getSourceForApiClass()); - - static StaticCompilationUnitProvider cuNewPackage = new StaticCompilationUnitProvider( - "java.newpackage", "Test", getSourceForTest()); - - static StaticCompilationUnitProvider cuNonApiClass = new StaticCompilationUnitProvider( - "test.apicontainer", "NonApiClass", getSourceForNonApiClass()); - static StaticCompilationUnitProvider cuNonApiPackage = new StaticCompilationUnitProvider( - "test.nonapipackage", "TestClass", getSourceForTestClass()); - - static StaticCompilationUnitProvider cuObject = new StaticCompilationUnitProvider( - "java.lang", "Object", getSourceForObject()); + static StaticCompilationUnit cuApiClass = new StaticCompilationUnit( + "test.apicontainer.ApiClass", getSourceForApiClass()); + static StaticCompilationUnit cuNonApiClass = new StaticCompilationUnit( + "test.apicontainer.NonApiClass", getSourceForNonApiClass()); + static StaticCompilationUnit cuNonApiPackage = new StaticCompilationUnit( + "test.nonapipackage.TestClass", getSourceForTestClass()); + static StaticCompilationUnit cuObject = new StaticCompilationUnit( + "java.lang.Object", getSourceForObject()); + static StaticCompilationUnit cuNewPackage = new StaticCompilationUnit( + "java.newpackage.Test", getSourceForTest()); private static JAbstractMethod getMethodByName(String name, ApiClass apiClass) { return (apiClass.getApiMethodsByName(name, ApiClass.MethodType.METHOD).toArray( @@ -142,18 +143,20 @@ public TypeOracle getNewTypeOracleWithCompilationUnitsAdded() throws UnableToCompleteException { - // Build onto an empty type oracle. - TypeOracleBuilder builder1 = new TypeOracleBuilder(new CacheManager(null, - null, ApiCompatibilityChecker.DISABLE_CHECKS)); - builder1.addCompilationUnit(cuObject); - builder1.addCompilationUnit(cuNonApiClass); - builder1.addCompilationUnit(cuApiClass); - builder1.addCompilationUnit(cuNonApiPackage); - builder1.addCompilationUnit(cuNewPackage); AbstractTreeLogger logger = new PrintWriterTreeLogger(); logger.setMaxDetail(TreeLogger.ERROR); - TypeOracle typeOracle = builder1.build(logger); - return typeOracle; + + // Build onto an empty type oracle. + TypeOracleMediator mediator = new TypeOracleMediator(); + Set<CompilationUnit> units = new HashSet<CompilationUnit>(); + units.add(cuObject); + units.add(cuNonApiClass); + units.add(cuApiClass); + units.add(cuNonApiPackage); + units.add(cuNewPackage); + JdtCompiler.compile(units); + mediator.refresh(logger, units); + return mediator.getTypeOracle(); } /**
diff --git a/tools/benchmark-viewer/build.xml b/tools/benchmark-viewer/build.xml index 8d187fe..3fa38f6 100755 --- a/tools/benchmark-viewer/build.xml +++ b/tools/benchmark-viewer/build.xml
@@ -96,6 +96,7 @@ </targetfiles> <sequential> <java dir="${tools.build}" classname="com.google.gwt.dev.GWTCompiler" classpath="src:${gwt.user.jar}:${gwt.dev.jar}" fork="yes" failonerror="true"> + <jvmarg value="-Xmx256M"/> <arg value="-out" /> <arg file="${tools.build}/www" /> <arg value="${tools.module}" />
diff --git a/user/src/com/google/gwt/benchmarks/BenchmarkReport.java b/user/src/com/google/gwt/benchmarks/BenchmarkReport.java index 7f17f3a..3094d33 100644 --- a/user/src/com/google/gwt/benchmarks/BenchmarkReport.java +++ b/user/src/com/google/gwt/benchmarks/BenchmarkReport.java
@@ -193,16 +193,8 @@ return source; } - try { - return source.substring(method.getDeclStart(), method.getDeclEnd() + 1); - } catch (IndexOutOfBoundsException e) { - logger.log(TreeLogger.WARN, "Unable to parse " + method.getName(), e); - // Have seen this happen when the compiler read the source using one - // character encoding, and then this Parser read it in a different - // encoding. I don't know if there are other cases in which this can - // occur. - return null; - } + // TODO: search for the method manually? + return null; } }
diff --git a/user/src/com/google/gwt/core/client/GWT.java b/user/src/com/google/gwt/core/client/GWT.java index b4652d7..b00f5b5 100644 --- a/user/src/com/google/gwt/core/client/GWT.java +++ b/user/src/com/google/gwt/core/client/GWT.java
@@ -42,7 +42,29 @@ void onUncaughtException(Throwable e); } - // web mode default is to let the exception go + /** + * An {@link UncaughtExceptionHandler} that logs errors to + * {@link GWT#log(String, Throwable)}. This is the default exception handler + * in hosted mode. In web mode, the default exception handler is + * <code>null</code>. + */ + private static final class DefaultUncaughtExceptionHandler implements + UncaughtExceptionHandler { + public void onUncaughtException(Throwable e) { + log("Uncaught exception escaped", e); + } + } + + /** + * Always <code>null</code> in web mode; in hosted mode provides the + * implementation for certain methods. + */ + private static GWTBridge sGWTBridge = null; + + /** + * Defaults to <code>null</code> in web mode and an instance of + * {@link DefaultUncaughtExceptionHandler} in hosted mode. + */ private static UncaughtExceptionHandler sUncaughtExceptionHandler = null; /** @@ -61,16 +83,20 @@ */ @SuppressWarnings("unused") public static <T> T create(Class<?> classLiteral) { - /* - * In web mode, the compiler directly replaces calls to this method with a - * new Object() type expression of the correct rebound type. - */ - throw new UnsupportedOperationException( - "ERROR: GWT.create() is only usable in client code! It cannot be called, " - + "for example, from server code. If you are running a unit test, " - + "check that your test case extends GWTTestCase and that GWT.create() " - + "is not called from within an initializer, constructor, or " - + "setUp()/tearDown()."); + if (sGWTBridge == null) { + /* + * In web mode, the compiler directly replaces calls to this method with a + * new Object() type expression of the correct rebound type. + */ + throw new UnsupportedOperationException( + "ERROR: GWT.create() is only usable in client code! It cannot be called, " + + "for example, from server code. If you are running a unit test, " + + "check that your test case extends GWTTestCase and that GWT.create() " + + "is not called from within an initializer, constructor, or " + + "setUp()/tearDown()."); + } else { + return sGWTBridge.<T> create(classLiteral); + } } /** @@ -126,9 +152,13 @@ return sUncaughtExceptionHandler; } - public static native String getVersion() /*-{ - return $gwt_version; - }-*/; + public static String getVersion() { + if (sGWTBridge == null) { + return getVersion0(); + } else { + return sGWTBridge.getVersion(); + } + } /** * Returns <code>true</code> when running inside the normal GWT environment, @@ -137,16 +167,15 @@ * on the server, or during the bootstrap sequence of a GWTTestCase test. */ public static boolean isClient() { - // Replaced with "true" by compiler and hosted mode. - return false; + // Replaced with "true" by compiler. + return sGWTBridge != null; } /** * Determines whether or not the running program is script or bytecode. */ public static boolean isScript() { - // Will return false in hosted mode. - return isClient() && true; + return isClient() && sGWTBridge == null; } /** @@ -155,7 +184,9 @@ */ @SuppressWarnings("unused") public static void log(String message, Throwable e) { - // intentionally empty in web mode. + if (sGWTBridge != null) { + sGWTBridge.log(message, e); + } } /** @@ -170,4 +201,19 @@ UncaughtExceptionHandler handler) { sUncaughtExceptionHandler = handler; } + + /** + * Called via reflection in hosted mode; do not every call this method in web + * mode. + */ + static void setBridge(GWTBridge bridge) { + sGWTBridge = bridge; + if (bridge != null) { + setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler()); + } + } + + private static native String getVersion0() /*-{ + return $gwt_version; + }-*/; }
diff --git a/user/src/com/google/gwt/core/client/GWT.java-hosted b/user/src/com/google/gwt/core/client/GWT.java-hosted deleted file mode 100644 index 0c1dc4b..0000000 --- a/user/src/com/google/gwt/core/client/GWT.java-hosted +++ /dev/null
@@ -1,86 +0,0 @@ -/* - * Copyright 2008 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.core.client; - -import com.google.gwt.core.client.impl.Impl; -import com.google.gwt.dev.shell.ShellGWT; - -/** - * The hosted mode implementation of the magic GWT class, with different - * implementations of certain core methods. - */ -public final class GWT { - public interface UncaughtExceptionHandler { - void onUncaughtException(Throwable e); - } - - // hosted mode default is to log the exception to the log window - private static UncaughtExceptionHandler sUncaughtExceptionHandler = new UncaughtExceptionHandler() { - public void onUncaughtException(Throwable e) { - log("Uncaught exception escaped", e); - } - }; - - public static <T> T create(Class<?> classLiteral) { - // deferred binding at runtime - return ShellGWT.create(classLiteral); - } - - public static String getHostPageBaseURL() { - return Impl.getHostPageBaseURL(); - } - - public static String getModuleBaseURL() { - return Impl.getModuleBaseURL(); - } - - public static String getModuleName() { - return Impl.getModuleName(); - } - - public static String getTypeName(Object o) { - // uses reflection in hosted mode - return ShellGWT.getTypeName(o); - } - - public static UncaughtExceptionHandler getUncaughtExceptionHandler() { - return sUncaughtExceptionHandler; - } - - public static String getVersion() { - return ShellGWT.getVersion(); - }; - - public static boolean isClient() { - // true in hosted mode - return true; - } - - public static boolean isScript() { - // false in hosted mode - return false; - } - - public static void log(String message, Throwable e) { - // logs to the shell logger in hosted mode - ShellGWT.log(message, e); - } - - public static void setUncaughtExceptionHandler( - UncaughtExceptionHandler handler) { - sUncaughtExceptionHandler = handler; - } -}
diff --git a/user/src/com/google/gwt/user/client/impl/HTTPRequestImplIE6.java b/user/src/com/google/gwt/user/client/impl/HTTPRequestImplIE6.java index 35f38d5..ea4356d 100644 --- a/user/src/com/google/gwt/user/client/impl/HTTPRequestImplIE6.java +++ b/user/src/com/google/gwt/user/client/impl/HTTPRequestImplIE6.java
@@ -18,8 +18,7 @@ import com.google.gwt.core.client.JavaScriptObject; /** - * Internet Explorer 6 implementation of - * {@link com.google.gwt.user.client.impl.HttpRequestImpl}. + * Internet Explorer 6 implementation of {@link HTTPRequestImpl}. */ class HTTPRequestImplIE6 extends HTTPRequestImpl {
diff --git a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleImpl.java b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleImpl.java index faf66dc..41f1653 100644 --- a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleImpl.java +++ b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleImpl.java
@@ -21,7 +21,7 @@ import com.google.gwt.core.ext.typeinfo.JParameterizedType; import com.google.gwt.core.ext.typeinfo.JType; import com.google.gwt.core.ext.typeinfo.TypeOracle; -import com.google.gwt.dev.jdt.TypeOracleBuilder; +import com.google.gwt.dev.javac.TypeOracleMediator; import com.google.gwt.dev.util.Util; import java.io.UnsupportedEncodingException; @@ -144,7 +144,7 @@ } public String getSerializedTypeName(JType type) { - return TypeOracleBuilder.computeBinaryClassName(type); + return TypeOracleMediator.computeBinaryClassName(type); } public String getTypeSerializerQualifiedName(JClassType serviceIntf) {
diff --git a/user/src/com/google/gwt/user/rebind/ui/ImageBundleGenerator.java b/user/src/com/google/gwt/user/rebind/ui/ImageBundleGenerator.java index 1513790..258d001 100644 --- a/user/src/com/google/gwt/user/rebind/ui/ImageBundleGenerator.java +++ b/user/src/com/google/gwt/user/rebind/ui/ImageBundleGenerator.java
@@ -19,7 +19,6 @@ import com.google.gwt.core.ext.GeneratorContext; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.JMethod; import com.google.gwt.core.ext.typeinfo.NotFoundException; @@ -190,16 +189,6 @@ return baseName + "_generatedBundle"; } - private int countLines(char[] source, int declStartIndex) { - int lineNum = 1; - for (int i = 0; i < declStartIndex; ++i) { - if (source[i] == '\n') { - ++lineNum; - } - } - return lineNum; - } - private void generateImageMethod(ImageBundleBuilder compositeImage, SourceWriter sw, JMethod method, String imgResName) { @@ -287,11 +276,8 @@ List<String> imageResNames = new ArrayList<String>(); for (JMethod method : imageMethods) { - CompilationUnitProvider unit = method.getEnclosingType().getCompilationUnit(); - String sourceFile = unit.getLocation(); - int lineNum = countLines(unit.getSource(), method.getDeclStart()); String branchMsg = "Analyzing method '" + method.getName() - + "' beginning on line " + lineNum + " of " + sourceFile; + + "' in type " + userType.getQualifiedSourceName(); TreeLogger branch = logger.branch(TreeLogger.DEBUG, branchMsg, null); // Verify that this method is valid on an image bundle.
diff --git a/user/test/com/google/gwt/dev/cfg/PublicTagTest.java b/user/test/com/google/gwt/dev/cfg/PublicTagTest.java index 1af2ad6..03e189d 100644 --- a/user/test/com/google/gwt/dev/cfg/PublicTagTest.java +++ b/user/test/com/google/gwt/dev/cfg/PublicTagTest.java
@@ -15,15 +15,13 @@ */ package com.google.gwt.dev.cfg; +import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.dev.GWTCompiler; -import com.google.gwt.dev.util.Util; -import com.google.gwt.dev.util.log.AbstractTreeLogger; import com.google.gwt.dev.util.log.PrintWriterTreeLogger; import junit.framework.TestCase; -import java.io.File; +import java.io.PrintWriter; /** * Tests various permutations of the GWT module's &public& tag, @@ -31,82 +29,50 @@ */ public class PublicTagTest extends TestCase { - /** - * Provides a convenient interface to the {@link GWTCompiler}. This test - * cannot simply call {@link GWTCompiler#main(String[])} since it always - * terminates with a call to {@link System#exit(int)}. - */ - private static class Compiler extends GWTCompiler { - /** - * Run the {@link GWTCompiler} with the specified arguments. - * - * @param args arguments passed to the compiler. - */ - static void compile(String[] args) { - try { - final Compiler compiler = new Compiler(); - if (compiler.processArgs(args)) { - final AbstractTreeLogger logger = new PrintWriterTreeLogger(); - logger.setMaxDetail(compiler.getLogLevel()); - compiler.distill(logger, ModuleDefLoader.loadFromClassPath(logger, - compiler.getModuleName())); - } - } catch (UnableToCompleteException e) { - throw new RuntimeException("Compilation failed.", e); - } - } + private static TreeLogger getRootLogger() { + PrintWriterTreeLogger logger = new PrintWriterTreeLogger(new PrintWriter( + System.err, true)); + logger.setMaxDetail(TreeLogger.ERROR); + return logger; + } + + private final ModuleDef moduleDef; + + public PublicTagTest() throws UnableToCompleteException { + // Module has the same name as this class. + String moduleName = getClass().getCanonicalName(); + moduleDef = ModuleDefLoader.loadFromClassPath(getRootLogger(), moduleName); } public void testPublicTag() { - // Find the current directory - String userDir = System.getProperty("user.dir"); - assertNotNull(userDir); - File curDir = new File(userDir); - assertTrue(curDir.isDirectory()); + assertNotNull(moduleDef.findPublicFile("good0.html")); + assertNotNull(moduleDef.findPublicFile("good1.html")); + assertNotNull(moduleDef.findPublicFile("bar/good.html")); + assertNotNull(moduleDef.findPublicFile("good2.html")); + assertNotNull(moduleDef.findPublicFile("good3.html")); + assertNotNull(moduleDef.findPublicFile("good4.html")); + assertNotNull(moduleDef.findPublicFile("good5.html")); + assertNotNull(moduleDef.findPublicFile("good6.html")); + assertNotNull(moduleDef.findPublicFile("good7.html")); + assertNotNull(moduleDef.findPublicFile("good8.html")); + assertNotNull(moduleDef.findPublicFile("good10.html")); + assertNotNull(moduleDef.findPublicFile("good11.html")); + assertNotNull(moduleDef.findPublicFile("good9.html")); + assertNotNull(moduleDef.findPublicFile("bar/CVS/good.html")); + assertNotNull(moduleDef.findPublicFile("CVS/good.html")); + assertNotNull(moduleDef.findPublicFile("GOOD/bar/GOOD/good.html")); + assertNotNull(moduleDef.findPublicFile("GOOD/good.html")); - // Our module name is the same as this class's name - String moduleName = PublicTagTest.class.getName(); - - // Find our module output directory and delete it - File moduleDir = new File(curDir, "www/" + moduleName); - if (moduleDir.exists()) { - Util.recursiveDelete(moduleDir, false); - } - assertFalse(moduleDir.exists()); - - // Compile the dummy app; suppress output to stdout - Compiler.compile(new String[] { - moduleName, "-logLevel", "ERROR", "-out", "www"}); - - // Check the output folder - assertTrue(new File(moduleDir, "good0.html").exists()); - assertTrue(new File(moduleDir, "good1.html").exists()); - assertTrue(new File(moduleDir, "bar/good.html").exists()); - assertTrue(new File(moduleDir, "good2.html").exists()); - assertTrue(new File(moduleDir, "good3.html").exists()); - assertTrue(new File(moduleDir, "good4.html").exists()); - assertTrue(new File(moduleDir, "good5.html").exists()); - assertTrue(new File(moduleDir, "good6.html").exists()); - assertTrue(new File(moduleDir, "good7.html").exists()); - assertTrue(new File(moduleDir, "good8.html").exists()); - assertTrue(new File(moduleDir, "good10.html").exists()); - assertTrue(new File(moduleDir, "good11.html").exists()); - assertTrue(new File(moduleDir, "good9.html").exists()); - assertTrue(new File(moduleDir, "bar/CVS/good.html").exists()); - assertTrue(new File(moduleDir, "CVS/good.html").exists()); - assertTrue(new File(moduleDir, "GOOD/bar/GOOD/good.html").exists()); - assertTrue(new File(moduleDir, "GOOD/good.html").exists()); - - assertFalse(new File(moduleDir, "bad.Html").exists()); - assertFalse(new File(moduleDir, "bar/CVS/bad.html").exists()); - assertFalse(new File(moduleDir, "CVS/bad.html").exists()); - assertFalse(new File(moduleDir, "bad1.html").exists()); - assertFalse(new File(moduleDir, "bad2.html").exists()); - assertFalse(new File(moduleDir, "bad3.html").exists()); - assertFalse(new File(moduleDir, "bad.html").exists()); - assertFalse(new File(moduleDir, "bar/bad.html").exists()); - assertFalse(new File(moduleDir, "GOOD/bar/bad.html").exists()); - assertFalse(new File(moduleDir, "GOOD/bar/GOOD/bar/bad.html").exists()); + assertNull(moduleDef.findPublicFile("bad.Html")); + assertNull(moduleDef.findPublicFile("bar/CVS/bad.html")); + assertNull(moduleDef.findPublicFile("CVS/bad.html")); + assertNull(moduleDef.findPublicFile("bad1.html")); + assertNull(moduleDef.findPublicFile("bad2.html")); + assertNull(moduleDef.findPublicFile("bad3.html")); + assertNull(moduleDef.findPublicFile("bad.html")); + assertNull(moduleDef.findPublicFile("bar/bad.html")); + assertNull(moduleDef.findPublicFile("GOOD/bar/bad.html")); + assertNull(moduleDef.findPublicFile("GOOD/bar/GOOD/bar/bad.html")); } }
diff --git a/user/test/com/google/gwt/dev/cfg/SourceTagTest.java b/user/test/com/google/gwt/dev/cfg/SourceTagTest.java index 1939a32..70bda16 100644 --- a/user/test/com/google/gwt/dev/cfg/SourceTagTest.java +++ b/user/test/com/google/gwt/dev/cfg/SourceTagTest.java
@@ -35,6 +35,6 @@ * logical path would be java/lang/Object. */ protected String getLogicalPath(Class<?> clazz) { - return clazz.getCanonicalName().replace('.', '/') + ".java"; + return clazz.getCanonicalName(); } }
diff --git a/user/test/com/google/gwt/dev/cfg/SuperSourceTagTest.java b/user/test/com/google/gwt/dev/cfg/SuperSourceTagTest.java index 7fdc6af..80cedff 100644 --- a/user/test/com/google/gwt/dev/cfg/SuperSourceTagTest.java +++ b/user/test/com/google/gwt/dev/cfg/SuperSourceTagTest.java
@@ -37,8 +37,7 @@ protected String getLogicalPath(Class<?> clazz) { String name = clazz.getCanonicalName(); name = name.substring(getClass().getPackage().getName().length() + 1); - name = name.replace('.', '/') + ".java"; - name = name.replaceFirst("test/\\w+/", ""); + name = name.replaceFirst("test\\.\\w+\\.", ""); return name; } }
diff --git a/user/test/com/google/gwt/dev/cfg/TestSuperAndSourceTags.java b/user/test/com/google/gwt/dev/cfg/TestSuperAndSourceTags.java index e4c326c..9a76d25 100644 --- a/user/test/com/google/gwt/dev/cfg/TestSuperAndSourceTags.java +++ b/user/test/com/google/gwt/dev/cfg/TestSuperAndSourceTags.java
@@ -70,8 +70,8 @@ } /** - * Returns the logical path for a class. This method is implemented by the - * subclasses because source and super-source compute logical paths + * Returns the logical path for a class. This method is implemented by the + * subclasses because source and super-source compute logical paths * differently. */ protected abstract String getLogicalPath(Class<?> clazz);