Author: [email protected] Date: Mon Jul 6 14:45:31 2009 New Revision: 5675 Added: trunk/dev/core/src/com/google/gwt/dev/javac/ArtificialRescueChecker.java trunk/dev/core/super/com/google/gwt/core/client/impl/ trunk/dev/core/super/com/google/gwt/core/client/impl/ArtificialRescue.java trunk/dev/core/test/com/google/gwt/dev/javac/ArtificialRescueCheckerTest.java trunk/dev/core/test/com/google/gwt/dev/javac/CheckerTestCase.java Modified: trunk/dev/core/src/com/google/gwt/dev/javac/CompilationUnitInvalidator.java trunk/dev/core/src/com/google/gwt/dev/jdt/AbstractCompiler.java trunk/dev/core/src/com/google/gwt/dev/jdt/WebModeCompilerFrontEnd.java trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java trunk/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java trunk/dev/core/src/com/google/gwt/dev/js/JsUnusedFunctionRemover.java trunk/dev/core/src/com/google/gwt/dev/js/ast/JsFunction.java trunk/dev/core/test/com/google/gwt/dev/javac/JsniCheckerTest.java trunk/user/src/com/google/gwt/core/client/impl/Impl.java
Log: Add @ArtificialRescue annotation to the GWT compiler. Patch by: bobv Review by: scottb Added: trunk/dev/core/src/com/google/gwt/dev/javac/ArtificialRescueChecker.java ============================================================================== --- (empty file) +++ trunk/dev/core/src/com/google/gwt/dev/javac/ArtificialRescueChecker.java Mon Jul 6 14:45:31 2009 @@ -0,0 +1,377 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.dev.javac; + +import com.google.gwt.core.client.impl.ArtificialRescue; +import com.google.gwt.dev.jjs.InternalCompilerException; +import com.google.gwt.dev.util.Empty; +import com.google.gwt.dev.util.JsniRef; +import com.google.gwt.dev.util.collect.Lists; + +import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.internal.compiler.ASTVisitor; +import org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer; +import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.ast.Expression; +import org.eclipse.jdt.internal.compiler.ast.MemberValuePair; +import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation; +import org.eclipse.jdt.internal.compiler.ast.StringLiteral; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding; +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.MethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons; +import org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding; +import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; + +import java.util.List; + +/** + * Checks the validity of ArtificialRescue annotations. + * + * <ul> + * <li>(1) The ArtificialRescue annotation is only used in generated code.</li> + * <li>(2) The className value names a type known to GWT (ignoring access rules) + * </li> + * <li>(3) The methods and fields of the type are known to GWT</li> + * </ul> + */ +public class ArtificialRescueChecker { + private class Visitor extends ASTVisitor { + + { + assert collectTypes || reportErrors : "No work to be done"; + } + + @Override + public void endVisit(TypeDeclaration localTypeDeclaration, BlockScope scope) { + processType(localTypeDeclaration); + } + + @Override + public void endVisit(TypeDeclaration memberTypeDeclaration, ClassScope scope) { + processType(memberTypeDeclaration); + } + + @Override + public void endVisit(TypeDeclaration typeDeclaration, + CompilationUnitScope scope) { + processType(typeDeclaration); + } + + private void processArtificialRescue(Annotation rescue) { + if (!allowArtificialRescue) { + // Goal (1) + GWTProblem.recordInCud(rescue, cud, onlyGeneratedCode(), null); + return; + } + + String className = null; + String[] methods = Empty.STRINGS; + String[] fields = Empty.STRINGS; + for (MemberValuePair pair : rescue.memberValuePairs()) { + String name = String.valueOf(pair.name); + if ("className".equals(name)) { + className = pair.value.constant.stringValue(); + } else if ("methods".equals(name)) { + methods = stringArrayFromValue(pair.value); + } else if ("fields".equals(name)) { + fields = stringArrayFromValue(pair.value); + } + } + + assert className != null; + + if (collectTypes) { + referencedTypes = Lists.add(referencedTypes, className); + } + + if (!reportErrors) { + // Nothing else to do + return; + } + + // Goal (2) + // Strip off any array-like extensions and just find base type + boolean isArray = false; + while (className.endsWith("[]")) { + className = className.substring(0, className.length() - 2); + isArray = true; + } + + // Fix JSNI primitive type names to something JDT will understand + if (isArray && className.length() == 1) { + switch (className.charAt(0)) { + case 'B': + className = "byte"; + break; + case 'C': + className = "char"; + break; + case 'D': + className = "double"; + break; + case 'F': + className = "float"; + break; + case 'I': + className = "int"; + break; + case 'J': + className = "long"; + break; + case 'S': + className = "short"; + break; + case 'Z': + className = "boolean"; + break; + } + } + + char[][] compoundName = CharOperation.splitOn('.', + className.toCharArray()); + TypeBinding typeBinding = cud.scope.getType(compoundName, + compoundName.length); + if (typeBinding == null) { + GWTProblem.recordInCud(rescue, cud, notFound(className), null); + } else if (typeBinding instanceof ProblemReferenceBinding) { + ProblemReferenceBinding problem = (ProblemReferenceBinding) typeBinding; + if (problem.problemId() == ProblemReasons.NotVisible) { + // Ignore + } else if (problem.problemId() == ProblemReasons.NotFound) { + GWTProblem.recordInCud(rescue, cud, notFound(className), null); + } else { + GWTProblem.recordInCud(rescue, cud, + unknownProblem(className, problem), null); + } + } else if (typeBinding instanceof BaseTypeBinding) { + // No methods or fields on primitive types (3) + if (methods.length > 0) { + GWTProblem.recordInCud(rescue, cud, noMethodsAllowed(), null); + } + + if (fields.length > 0) { + GWTProblem.recordInCud(rescue, cud, noFieldsAllowed(), null); + } + } else if (typeBinding instanceof ReferenceBinding) { + ReferenceBinding ref = (ReferenceBinding) typeBinding; + + if (isArray) { + // No methods or fields on array types (3) + if (methods.length > 0) { + GWTProblem.recordInCud(rescue, cud, noMethodsAllowed(), null); + } + + if (fields.length > 0) { + GWTProblem.recordInCud(rescue, cud, noFieldsAllowed(), null); + } + } else { + // Check methods on reference types (3) + for (String method : methods) { + if (method.contains("@")) { + GWTProblem.recordInCud(rescue, cud, nameAndTypesOnly(), null); + continue; + } + JsniRef jsni = JsniRef.parse("@foo::" + method); + if (jsni == null) { + GWTProblem.recordInCud(rescue, cud, badMethodSignature(method), + null); + continue; + } + + if (jsni.memberName().equals( + String.valueOf(ref.compoundName[ref.compoundName.length - 1]))) { + // Constructor + } else { + MethodBinding[] methodBindings = ref.getMethods(jsni.memberName().toCharArray()); + if (methodBindings == null || methodBindings.length == 0) { + GWTProblem.recordInCud(rescue, cud, noMethod(className, + jsni.memberName()), null); + continue; + } + } + } + + // Check fields on reference types (3) + for (String field : fields) { + if (ref.getField(field.toCharArray(), false) == null) { + GWTProblem.recordInCud(rescue, cud, unknownField(field), null); + } + } + } + } + } + + /** + * Examine a TypeDeclaration for ArtificialRescue annotations. Delegates to + * {...@link #processArtificialRescue(Annotation)} to complete the processing. + */ + private void processType(TypeDeclaration x) { + if (x.annotations == null) { + return; + } + + for (Annotation a : x.annotations) { + if (!ArtificialRescue.class.getName().equals( + CharOperation.toString(((ReferenceBinding) a.resolvedType).compoundName))) { + continue; + } + + // Sometimes it's a SingleMemberAnnotation, other times it's not + Expression value = null; + if (a instanceof SingleMemberAnnotation) { + value = ((SingleMemberAnnotation) a).memberValue; + } else { + for (MemberValuePair pair : a.memberValuePairs()) { + if ("value".equals(String.valueOf(pair.name))) { + value = pair.value; + break; + } + } + } + + assert value != null; + if (value instanceof ArrayInitializer) { + for (Expression e : ((ArrayInitializer) value).expressions) { + processArtificialRescue((Annotation) e); + } + } else if (value instanceof Annotation) { + processArtificialRescue((Annotation) value); + } else { + throw new InternalCompilerException( + "Unable to process annotation with value of type " + + value.getClass().getName()); + } + + return; + } + } + + private String[] stringArrayFromValue(Expression value) { + if (value instanceof StringLiteral) { + return new String[] {value.constant.stringValue()}; + } else if (value instanceof ArrayInitializer) { + ArrayInitializer init = (ArrayInitializer) value; + String[] toReturn = new String[init.expressions == null ? 0 + : init.expressions.length]; + for (int i = 0; i < toReturn.length; i++) { + toReturn[i] = init.expressions[i].constant.stringValue(); + } + return toReturn; + } else { + throw new InternalCompilerException("Unhandled value type " + + value.getClass().getName()); + } + } + } + + /** + * Check the {...@link ArtificialRescue} annotations in a CompilationUnit. Errors + * are reported through {...@link GWTProblem}. + */ + public static void check(CompilationUnit cud) { + new ArtificialRescueChecker(cud).check(); + } + + /** + * Report all types named in {...@link ArtificialRescue} annotations in a CUD. No + * error checking is done. + */ + public static List<String> collectReferencedTypes( + CompilationUnitDeclaration cud) { + return new ArtificialRescueChecker(cud).collect(); + } + + static String badMethodSignature(String method) { + return "Bad method signature " + method; + } + + static String nameAndTypesOnly() { + return "Only method name and parameter types expected"; + } + + static String noFieldsAllowed() { + return "Cannot refer to fields on array or primitive types"; + } + + static String noMethod(String className, String methodName) { + return "No method named " + methodName + " in type " + className; + } + + static String noMethodsAllowed() { + return "Cannot refer to methods on array or primitive types"; + } + + static String notFound(String className) { + return "Could not find type " + className; + } + + static String onlyGeneratedCode() { + return "The " + ArtificialRescue.class.getName() + + " annotation may only be used in generated code and its use" + + " by third parties is not supported."; + } + + static String unknownField(String field) { + return "Unknown field " + field; + } + + static String unknownProblem(String className, ProblemReferenceBinding problem) { + return "Unknown problem: " + + ProblemReferenceBinding.problemReasonString(problem.problemId()) + + " " + className; + } + + private final boolean allowArtificialRescue; + + private boolean collectTypes; + + private boolean reportErrors; + + private final CompilationUnitDeclaration cud; + + private List<String> referencedTypes; + + private ArtificialRescueChecker(CompilationUnit unit) { + allowArtificialRescue = unit.isGenerated(); + cud = unit.getJdtCud(); + } + + private ArtificialRescueChecker(CompilationUnitDeclaration cud) { + allowArtificialRescue = true; + this.cud = cud; + } + + private void check() { + collectTypes = false; + reportErrors = true; + cud.traverse(new Visitor(), cud.scope); + } + + private List<String> collect() { + collectTypes = true; + referencedTypes = Lists.create(); + reportErrors = false; + cud.traverse(new Visitor(), cud.scope); + return referencedTypes; + } + +} Modified: trunk/dev/core/src/com/google/gwt/dev/javac/CompilationUnitInvalidator.java ============================================================================== --- trunk/dev/core/src/com/google/gwt/dev/javac/CompilationUnitInvalidator.java (original) +++ trunk/dev/core/src/com/google/gwt/dev/javac/CompilationUnitInvalidator.java Mon Jul 6 14:45:31 2009 @@ -202,6 +202,7 @@ CompilationUnitDeclaration jdtCud = unit.getJdtCud(); JSORestrictionsChecker.check(state.jsoState, jdtCud); JsniChecker.check(jdtCud); + ArtificialRescueChecker.check(unit); BinaryTypeReferenceRestrictionsChecker.check(jdtCud, validBinaryTypeNames); } Modified: trunk/dev/core/src/com/google/gwt/dev/jdt/AbstractCompiler.java ============================================================================== --- trunk/dev/core/src/com/google/gwt/dev/jdt/AbstractCompiler.java (original) +++ trunk/dev/core/src/com/google/gwt/dev/jdt/AbstractCompiler.java Mon Jul 6 14:45:31 2009 @@ -156,32 +156,13 @@ // Examine the cud for magic types. // String[] typeNames = doFindAdditionalTypesUsingJsni(logger, unit); - - // Accept each new compilation unit. - // - for (int i = 0; i < typeNames.length; i++) { - String typeName = typeNames[i]; - final String msg = "Need additional type '" + typeName + "'"; - logger.log(TreeLogger.SPAM, msg, null); - - resolvePossiblyNestedType(typeName); - } + addAdditionalTypes(logger, typeNames); typeNames = doFindAdditionalTypesUsingRebinds(logger, unit); + addAdditionalTypes(logger, typeNames); - // Accept each new compilation unit, and check for instantiability - // - for (int i = 0; i < typeNames.length; i++) { - String typeName = typeNames[i]; - final String msg = "Need additional type '" + typeName + "'"; - logger.log(TreeLogger.SPAM, msg, null); - - // This causes the compiler to find the additional type, possibly - // winding its back to ask for the compilation unit from the source - // oracle. - // - resolvePossiblyNestedType(typeName); - } + typeNames = doFindAdditionalTypesUsingArtificialRescues(logger, unit); + addAdditionalTypes(logger, typeNames); doCompilationUnitDeclarationValidation(unit, logger); @@ -194,6 +175,21 @@ jdtProcessNanos += System.nanoTime() - processBeginNanos; } + /** + * Helper method for process() that receives the types found by magic. This + * causes the compiler to find the additional type, possibly winding its + * back to ask for the compilation unit from the source oracle. + */ + private void addAdditionalTypes(TreeLogger logger, String[] typeNames) { + for (int i = 0; i < typeNames.length; i++) { + String typeName = typeNames[i]; + final String msg = "Need additional type '" + typeName + "'"; + logger.log(TreeLogger.SPAM, msg, null); + + resolvePossiblyNestedType(typeName); + } + } + private void compile(ICompilationUnit[] units, Set<CompilationUnitDeclaration> cuds) { this.cuds = cuds; @@ -503,6 +499,13 @@ protected void doCompilationUnitDeclarationValidation( CompilationUnitDeclaration cud, TreeLogger logger) { // Do nothing by default. + } + + @SuppressWarnings("unused") + // overrider may use unused parameter + protected String[] doFindAdditionalTypesUsingArtificialRescues( + TreeLogger logger, CompilationUnitDeclaration cud) { + return Empty.STRINGS; } @SuppressWarnings("unused") Modified: trunk/dev/core/src/com/google/gwt/dev/jdt/WebModeCompilerFrontEnd.java ============================================================================== --- trunk/dev/core/src/com/google/gwt/dev/jdt/WebModeCompilerFrontEnd.java (original) +++ trunk/dev/core/src/com/google/gwt/dev/jdt/WebModeCompilerFrontEnd.java Mon Jul 6 14:45:31 2009 @@ -19,6 +19,7 @@ import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.TypeOracle; +import com.google.gwt.dev.javac.ArtificialRescueChecker; import com.google.gwt.dev.javac.CompilationState; import com.google.gwt.dev.javac.CompilationUnit; import com.google.gwt.dev.javac.CompiledClass; @@ -142,6 +143,14 @@ * Anything that makes it here was already checked by AstCompiler while * building TypeOracle; no need to rerun checks. */ + } + + @Override + protected String[] doFindAdditionalTypesUsingArtificialRescues( + TreeLogger logger, CompilationUnitDeclaration cud) { + List<String> types = ArtificialRescueChecker.collectReferencedTypes(cud); + return types.isEmpty() ? Empty.STRINGS + : types.toArray(new String[types.size()]); } /** Modified: trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java ============================================================================== --- trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java (original) +++ trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java Mon Jul 6 14:45:31 2009 @@ -31,6 +31,12 @@ public abstract class JDeclaredType extends JReferenceType { /** + * The other nodes that this node should implicitly rescue. Special + * serialization treatment. + */ + protected transient List<JNode> artificialRescues = Lists.create(); + + /** * This type's fields. Special serialization treatment. */ protected transient List<JField> fields = Lists.create(); @@ -60,6 +66,10 @@ super(info, name); } + public void addArtificialRescue(JNode node) { + artificialRescues = Lists.add(artificialRescues, node); + } + /** * Adds a field to this type. */ @@ -91,9 +101,9 @@ * statically know the clinit method has already run when: * <ol> * <li><code>this == targetType</code></li> - * <li><code>this</code> is a subclass of <code>targetType</code>, - * because my clinit would have already run this <code>targetType</code>'s - * clinit; see JLS 12.4</li> + * <li><code>this</code> is a subclass of <code>targetType</code>, because my + * clinit would have already run this <code>targetType</code>'s clinit; see + * JLS 12.4</li> * </ol> */ public boolean checkClinitTo(JDeclaredType targetType) { @@ -118,6 +128,10 @@ return true; } + public List<JNode> getArtificialRescues() { + return artificialRescues; + } + /** * Returns this type's fields;does not include fields defined in a super type * unless they are overridden by this type. @@ -168,8 +182,7 @@ } /** - * Returns <code>true</code> when this class's clinit must be run - * dynamically. + * Returns <code>true</code> when this class's clinit must be run dynamically. */ public boolean hasClinit() { return hasClinit; @@ -231,9 +244,10 @@ ClassNotFoundException { fields = (List<JField>) stream.readObject(); methods = (List<JMethod>) stream.readObject(); + artificialRescues = (List<JNode>) stream.readObject(); } - /** +/** * See {...@link #writeMethodBodies(ObjectOutputStream). * * @see #writeMethodBodies(ObjectOutputStream) @@ -265,6 +279,7 @@ void writeMembers(ObjectOutputStream stream) throws IOException { stream.writeObject(fields); stream.writeObject(methods); + stream.writeObject(artificialRescues); } /** Modified: trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java ============================================================================== --- trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java (original) +++ trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java Mon Jul 6 14:45:31 2009 @@ -1055,9 +1055,9 @@ } /** - * If <code>method</code> is a static impl method, returns the instance - * method that <code>method</code> is the implementation of. Otherwise, - * returns <code>null</code>. + * If <code>method</code> is a static impl method, returns the instance method + * that <code>method</code> is the implementation of. Otherwise, returns + * <code>null</code>. */ public JMethod staticImplFor(JMethod method) { return staticToInstanceMap.get(method); Modified: trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java ============================================================================== --- trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java (original) +++ trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java Mon Jul 6 14:45:31 2009 @@ -547,6 +547,19 @@ if (doVisit) { accept(type); + + if (type instanceof JDeclaredType) { + for (JNode artificial : ((JDeclaredType) type).getArtificialRescues()) { + if (artificial instanceof JReferenceType) { + rescue((JReferenceType) artificial, true, true); + rescue(program.getLiteralClass((JReferenceType) artificial).getField()); + } else if (artificial instanceof JVariable) { + rescue((JVariable) artificial); + } else if (artificial instanceof JMethod) { + rescue((JMethod) artificial); + } + } + } } } } Modified: trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java ============================================================================== --- trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java (original) +++ trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java Mon Jul 6 14:45:31 2009 @@ -15,6 +15,7 @@ */ package com.google.gwt.dev.jjs.impl; +import com.google.gwt.core.client.impl.ArtificialRescue; import com.google.gwt.dev.jjs.HasSourceInfo; import com.google.gwt.dev.jjs.InternalCompilerException; import com.google.gwt.dev.jjs.JJSOptions; @@ -93,14 +94,17 @@ import com.google.gwt.dev.js.ast.JsModVisitor; import com.google.gwt.dev.js.ast.JsNameRef; import com.google.gwt.dev.js.ast.JsProgram; +import com.google.gwt.dev.util.Empty; import com.google.gwt.dev.util.JsniRef; +import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.compiler.IProblem; import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.ast.AND_AND_Expression; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; +import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.ArrayAllocationExpression; import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer; import org.eclipse.jdt.internal.compiler.ast.ArrayReference; @@ -131,6 +135,7 @@ import org.eclipse.jdt.internal.compiler.ast.InstanceOfExpression; import org.eclipse.jdt.internal.compiler.ast.LabeledStatement; import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; +import org.eclipse.jdt.internal.compiler.ast.MemberValuePair; import org.eclipse.jdt.internal.compiler.ast.MessageSend; import org.eclipse.jdt.internal.compiler.ast.NullLiteral; import org.eclipse.jdt.internal.compiler.ast.OR_OR_Expression; @@ -141,8 +146,10 @@ import org.eclipse.jdt.internal.compiler.ast.QualifiedSuperReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedThisReference; import org.eclipse.jdt.internal.compiler.ast.ReturnStatement; +import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.Statement; +import org.eclipse.jdt.internal.compiler.ast.StringLiteral; import org.eclipse.jdt.internal.compiler.ast.SuperReference; import org.eclipse.jdt.internal.compiler.ast.SwitchStatement; import org.eclipse.jdt.internal.compiler.ast.SynchronizedStatement; @@ -471,6 +478,8 @@ processEnumType((JEnumType) currentClass); } + processArtificialRescues(x); + currentClassScope = null; currentClass = null; currentSeparatorPositions = null; @@ -2429,6 +2438,122 @@ return variable; } + private void processArtificialRescue(Annotation rescue) { + assert rescue != null; + + JReferenceType classType = null; + String[] fields = Empty.STRINGS; + boolean instantiable = false; + String[] methods = Empty.STRINGS; + String typeName = null; + + for (MemberValuePair pair : rescue.memberValuePairs()) { + String name = String.valueOf(pair.name); + Expression value = pair.value; + + if ("className".equals(name)) { + typeName = value.constant.stringValue(); + + // Invalid references should be caught in ArtificialRescueChecker + classType = (JReferenceType) program.getTypeFromJsniRef(typeName); + + } else if ("fields".equals(name)) { + if (value instanceof StringLiteral) { + fields = new String[] {value.constant.stringValue()}; + } else if (value instanceof ArrayInitializer) { + ArrayInitializer init = (ArrayInitializer) value; + fields = new String[init.expressions == null ? 0 + : init.expressions.length]; + for (int i = 0, j = fields.length; i < j; i++) { + fields[i] = init.expressions[i].constant.stringValue(); + } + } + + } else if ("instantiable".equals(name)) { + instantiable = value.constant.booleanValue(); + + } else if ("methods".equals(name)) { + if (value instanceof StringLiteral) { + methods = new String[] {value.constant.stringValue()}; + } else if (value instanceof ArrayInitializer) { + ArrayInitializer init = (ArrayInitializer) value; + methods = new String[init.expressions == null ? 0 + : init.expressions.length]; + for (int i = 0, j = methods.length; i < j; i++) { + methods[i] = init.expressions[i].constant.stringValue(); + } + } + } else { + throw new InternalCompilerException( + "Unknown Rescue annotation member " + name); + } + } + + assert classType != null : "classType " + typeName; + assert fields != null : "fields"; + assert methods != null : "methods"; + + if (instantiable) { + currentClass.addArtificialRescue(classType); + + // Make sure that a class literal for the type has been allocated + program.getLiteralClass(classType); + } + + if (classType instanceof JDeclaredType) { + List<String> toRescue = new ArrayList<String>(); + Collections.addAll(toRescue, fields); + Collections.addAll(toRescue, methods); + + for (String name : toRescue) { + JsniRef ref = JsniRef.parse("@" + classType.getName() + "::" + name); + final String[] errors = {null}; + HasEnclosingType node = JsniRefLookup.findJsniRefTarget(ref, program, + new JsniRefLookup.ErrorReporter() { + public void reportError(String error) { + errors[0] = error; + } + }); + if (errors[0] != null) { + // Should have been caught by ArtificialRescueChecker + throw new InternalCompilerException( + "Unable to artificially rescue " + name + ": " + errors[0]); + } + + currentClass.addArtificialRescue((JNode) node); + } + } + } + + private void processArtificialRescues(TypeDeclaration x) { + if (x.annotations == null) { + return; + } + + for (Annotation a : x.annotations) { + if (!ArtificialRescue.class.getName().equals( + CharOperation.toString(((ReferenceBinding) a.resolvedType).compoundName))) { + continue; + } + + Expression value = ((SingleMemberAnnotation) a).memberValue; + assert value != null; + if (value instanceof ArrayInitializer) { + for (Expression e : ((ArrayInitializer) value).expressions) { + processArtificialRescue((Annotation) e); + } + } else if (value instanceof Annotation) { + processArtificialRescue((Annotation) value); + } else { + throw new InternalCompilerException( + "Unable to process annotation with value of type " + + value.getClass().getName()); + } + + return; + } + } + /** * Helper for creating all JBinaryOperation. Several different JDT nodes can * result in binary operations: AND_AND_Expression, Assignment, @@ -2673,7 +2798,8 @@ } } - private HasEnclosingType findJsniRefTarget(final SourceInfo info, String ident) { + private HasEnclosingType findJsniRefTarget(final SourceInfo info, + String ident) { JsniRef parsed = JsniRef.parse(ident); if (parsed == null) { reportJsniError(info, methodDecl, Modified: trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java ============================================================================== --- trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java (original) +++ trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java Mon Jul 6 14:45:31 2009 @@ -60,6 +60,7 @@ import com.google.gwt.dev.jjs.ast.JNameOf; import com.google.gwt.dev.jjs.ast.JNewArray; import com.google.gwt.dev.jjs.ast.JNewInstance; +import com.google.gwt.dev.jjs.ast.JNode; import com.google.gwt.dev.jjs.ast.JParameter; import com.google.gwt.dev.jjs.ast.JParameterRef; import com.google.gwt.dev.jjs.ast.JPostfixOperation; @@ -133,6 +134,7 @@ import com.google.gwt.dev.js.ast.JsWhile; import com.google.gwt.dev.js.ast.JsVars.JsVar; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EnumMap; @@ -250,6 +252,12 @@ */ nullMethodName = topScope.declareName(nullMethod.getName()); names.put(nullMethod, nullMethodName); + + /* + * Make sure we record all of the program's array types since + * JProgram.traverse() doesn't iterate over them. + */ + accept(new ArrayList<JArrayType>(program.getAllArrayTypes())); } @Override @@ -643,6 +651,16 @@ if (!vars.isEmpty()) { globalStmts.add(vars); + } + + for (JNode node : x.getArtificialRescues()) { + if (node instanceof JMethod) { + JsName jsName = names.get(node); + if (jsName != null) { + JsFunction func = (JsFunction) jsName.getStaticRef(); + func.setArtificiallyRescued(true); + } + } } } Modified: trunk/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java ============================================================================== --- trunk/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java (original) +++ trunk/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java Mon Jul 6 14:45:31 2009 @@ -15,6 +15,7 @@ */ package com.google.gwt.dev.jjs.impl; +import com.google.gwt.dev.jjs.InternalCompilerException; import com.google.gwt.dev.jjs.SourceInfo; import com.google.gwt.dev.jjs.ast.CanBeStatic; import com.google.gwt.dev.jjs.ast.Context; @@ -143,12 +144,18 @@ @Override public void endVisit(JNameOf x, Context ctx) { HasName node = x.getNode(); - JReferenceType isType = node instanceof JReferenceType - ? (JReferenceType) node : null; - if (isType == null && !referencedNonTypes.contains(node)) { - ctx.replaceMe(program.getLiteralNull()); - } else if (isType != null - && !program.typeOracle.isInstantiatedType(isType)) { + boolean pruned; + if (node instanceof JField) { + pruned = isPruned((JField) node); + } else if (node instanceof JMethod) { + pruned = isPruned((JMethod) node); + } else if (node instanceof JReferenceType) { + pruned = !program.typeOracle.isInstantiatedType((JReferenceType) node); + } else { + throw new InternalCompilerException("Unhandled JNameOf node: " + node); + } + + if (pruned) { ctx.replaceMe(program.getLiteralNull()); } } Modified: trunk/dev/core/src/com/google/gwt/dev/js/JsUnusedFunctionRemover.java ============================================================================== --- trunk/dev/core/src/com/google/gwt/dev/js/JsUnusedFunctionRemover.java (original) +++ trunk/dev/core/src/com/google/gwt/dev/js/JsUnusedFunctionRemover.java Mon Jul 6 14:45:31 2009 @@ -43,7 +43,7 @@ @Override public void endVisit(JsFunction x, JsContext<JsExpression> ctx) { // Anonymous function, ignore it - if (x.getName() != null) { + if (x.getName() != null && !x.isArtificiallyRescued()) { toRemove.put(x.getName(), x); } } Modified: trunk/dev/core/src/com/google/gwt/dev/js/ast/JsFunction.java ============================================================================== --- trunk/dev/core/src/com/google/gwt/dev/js/ast/JsFunction.java (original) +++ trunk/dev/core/src/com/google/gwt/dev/js/ast/JsFunction.java Mon Jul 6 14:45:31 2009 @@ -35,6 +35,7 @@ protected JsBlock body; protected final List<JsParameter> params = new ArrayList<JsParameter>(); protected final JsScope scope; + private boolean artificiallyRescued; private boolean executeOnce; private boolean fromJava; private JsFunction impliedExecute; @@ -105,6 +106,10 @@ return name != null; } + public boolean isArtificiallyRescued() { + return artificiallyRescued; + } + public boolean isBooleanFalse() { return false; } @@ -123,6 +128,10 @@ public boolean isFromJava() { return fromJava; + } + + public void setArtificiallyRescued(boolean rescued) { + this.artificiallyRescued = rescued; } public void setBody(JsBlock body) { Added: trunk/dev/core/super/com/google/gwt/core/client/impl/ArtificialRescue.java ============================================================================== --- (empty file) +++ trunk/dev/core/super/com/google/gwt/core/client/impl/ArtificialRescue.java Mon Jul 6 14:45:31 2009 @@ -0,0 +1,54 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.core.client.impl; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +/** + * An annotation to specify that if a class is rescued, other types, methods, or + * fields should be rescued as well. This annotation is an implementation detail + * of the deRPC code and its use by third parties is not supported. + */ +...@target(ElementType.TYPE) +public @interface ArtificialRescue { + /** + * Specifies the elements of a single type to rescue. + */ + @Target(value = {}) + public @interface Rescue { + /** + * The class to be retained. Primitive array types should be referenced + * using their JSNI type name. + */ + String className(); + + boolean instantiable() default false; + + /** + * Fields are specified as raw names. That is, <code>fieldName</code>. + */ + String[] fields() default {}; + + /** + * Methods are specified as unqualified JSNI signatures. That is, + * <code>methodName(Lsome/Type;...)</code>. + */ + String[] methods() default {}; + } + + Rescue[] value(); +} Added: trunk/dev/core/test/com/google/gwt/dev/javac/ArtificialRescueCheckerTest.java ============================================================================== --- (empty file) +++ trunk/dev/core/test/com/google/gwt/dev/javac/ArtificialRescueCheckerTest.java Mon Jul 6 14:45:31 2009 @@ -0,0 +1,150 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.dev.javac; + +import com.google.gwt.core.client.impl.ArtificialRescue; + +/** + * Tests for the ArtificialRescueChecker. + */ +public class ArtificialRescueCheckerTest extends CheckerTestCase { + + public void testBadClassName() { + StringBuilder builder = builder(); + builder.append("@ArtificialRescue(@Rescue(className=\"Fail\"))\n"); + builder.append("class Buggy{}"); + shouldGenerateError(builder, targetClass(), 3, + ArtificialRescueChecker.notFound("Fail")); + } + + public void testBadFieldName() { + StringBuilder builder = builder(); + builder.append("@ArtificialRescue(@Rescue(className=\"Target\", fields=\"foo\"))\n"); + builder.append("class Buggy{}"); + shouldGenerateError(builder, targetClass(), 3, + ArtificialRescueChecker.unknownField("foo")); + } + + public void testBadMembersOnArray() { + StringBuilder builder = builder(); + builder.append("@ArtificialRescue(@Rescue(className=\"Target[]\", fields=\"foo\"))\n"); + builder.append("class Buggy{}"); + shouldGenerateError(builder, targetClass(), 3, + ArtificialRescueChecker.noFieldsAllowed()); + } + + public void testBadMethodName() { + StringBuilder builder = builder(); + builder.append("@ArtificialRescue(@Rescue(className=\"Target\", methods=\"blah()\"))\n"); + builder.append("class Buggy{}"); + shouldGenerateError(builder, targetClass(), 3, + ArtificialRescueChecker.noMethod("Target", "blah")); + } + + public void testBadMethodSignature() { + StringBuilder builder = builder(); + builder.append("@ArtificialRescue(@Rescue(className=\"Target\", methods=\"blah()()\"))\n"); + builder.append("class Buggy{}"); + shouldGenerateError(builder, targetClass(), 3, + ArtificialRescueChecker.badMethodSignature("blah()()")); + } + + public void testBadMethodSignatureFullyQualified() { + StringBuilder builder = builder(); + builder.append("@ArtificialRescue(@Rescue(className=\"Target\", methods=\"@Target::blah()\"))\n"); + builder.append("class Buggy{}"); + shouldGenerateError(builder, targetClass(), 3, + ArtificialRescueChecker.nameAndTypesOnly()); + } + + public void testOkArray() { + StringBuilder builder = builder(); + builder.append("@ArtificialRescue(@Rescue(className=\"Target[]\"))\n"); + builder.append("class Buggy{}"); + shouldGenerateNoError(builder, targetClass()); + } + + public void testOkConstructor() { + StringBuilder builder = builder(); + builder.append("@ArtificialRescue(@Rescue(className=\"Target\", methods=\"Target()\"))\n"); + builder.append("class Buggy{}"); + shouldGenerateNoError(builder, targetClass()); + } + + public void testOkInner() { + StringBuilder builder = builder(); + builder.append("@ArtificialRescue(@Rescue(className=\"Target.Inner\", methods=\"Target$Inner()\"))\n"); + builder.append("class Buggy{}"); + shouldGenerateNoError(builder, targetClass()); + } + + public void testOkOneField() { + StringBuilder builder = builder(); + builder.append("@ArtificialRescue(@Rescue(className=\"Target\", fields=\"i\"))\n"); + builder.append("class Buggy{}"); + shouldGenerateNoError(builder, targetClass()); + } + + public void testOkOneMethod() { + StringBuilder builder = builder(); + builder.append("@ArtificialRescue(@Rescue(className=\"Target\", methods=\"getI()\"))\n"); + builder.append("class Buggy{}"); + shouldGenerateNoError(builder, targetClass()); + } + + public void testOkPrimitiveArray() { + StringBuilder builder = builder(); + builder.append("@ArtificialRescue(@Rescue(className=\"Z[]\"))\n"); + builder.append("class Buggy{}"); + shouldGenerateNoError(builder, targetClass()); + } + + public void testOkTwoFields() { + StringBuilder builder = builder(); + builder.append("@ArtificialRescue(@Rescue(className=\"Target\", fields={\"i\", \"str\"}))\n"); + builder.append("class Buggy{}"); + shouldGenerateNoError(builder, targetClass()); + } + + public void testOkTwoMethods() { + StringBuilder builder = builder(); + builder.append("@ArtificialRescue(@Rescue(className=\"Target\", methods={\"getI()\", \"getI(Z)\"}))\n"); + builder.append("class Buggy{}"); + shouldGenerateNoError(builder, targetClass()); + } + + private StringBuilder builder() { + StringBuilder code = new StringBuilder(); + code.append("import " + ArtificialRescue.class.getCanonicalName() + ";\n"); + code.append("import " + ArtificialRescue.Rescue.class.getCanonicalName() + + ";\n"); + return code; + } + + private StringBuilder targetClass() { + StringBuilder targetClass = new StringBuilder(); + targetClass = new StringBuilder(); + targetClass.append("class Target {\n"); + targetClass.append(" public class Inner{}\n"); + targetClass.append(" private String str;\n"); + targetClass.append(" private int i;\n"); + targetClass.append(" public int getI() { return i; }\n"); + targetClass.append(" public int getI(boolean override) {return override ? 0 : i; }\n"); + targetClass.append(" private String getStr() {return str;}"); + targetClass.append("}"); + return targetClass; + } +} Added: trunk/dev/core/test/com/google/gwt/dev/javac/CheckerTestCase.java ============================================================================== --- (empty file) +++ trunk/dev/core/test/com/google/gwt/dev/javac/CheckerTestCase.java Mon Jul 6 14:45:31 2009 @@ -0,0 +1,108 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.dev.javac; + +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.core.ext.TreeLogger.Type; +import com.google.gwt.core.ext.typeinfo.TypeOracle; +import com.google.gwt.dev.util.UnitTestTreeLogger; + +import junit.framework.TestCase; + +import java.util.HashSet; +import java.util.Set; + +/** + * A base type for testing the code checkers. + */ +public class CheckerTestCase extends TestCase { + + protected void shouldGenerate(CharSequence buggyCode, CharSequence extraCode, + int line, Type logLevel, String logHeader, String message) { + UnitTestTreeLogger.Builder b = new UnitTestTreeLogger.Builder(); + b.setLowestLogLevel(logLevel); + if (message != null) { + b.expect(logLevel, logHeader + " in '/mock/Buggy'", null); + final String fullMessage = "Line " + line + ": " + message; + b.expect(logLevel, fullMessage, null); + } + UnitTestTreeLogger logger = b.createLogger(); + TypeOracle oracle = buildOracle(buggyCode, extraCode, logger); + logger.assertCorrectLogEntries(); + if (message != null && logLevel == TreeLogger.ERROR) { + assertNull("Buggy compilation unit not removed from type oracle", + oracle.findType("Buggy")); + } else { + assertNotNull("Buggy compilation unit removed with only a warning", + oracle.findType("Buggy")); + } + } + + protected void shouldGenerateError(CharSequence buggyCode, + CharSequence extraCode, int line, String message) { + shouldGenerate(buggyCode, extraCode, line, TreeLogger.ERROR, "Errors", + message); + } + + protected void shouldGenerateError(CharSequence buggyCode, int line, + String message) { + shouldGenerateError(buggyCode, null, line, message); + } + + protected void shouldGenerateNoError(CharSequence code) { + shouldGenerateNoError(code, null); + } + + protected void shouldGenerateNoError(CharSequence code, CharSequence extraCode) { + shouldGenerateError(code, extraCode, -1, null); + } + + protected void shouldGenerateNoWarning(CharSequence code) { + shouldGenerateWarning(code, -1, null); + } + + protected void shouldGenerateWarning(CharSequence buggyCode, + CharSequence extraCode, int line, String message) { + shouldGenerate(buggyCode, extraCode, line, TreeLogger.WARN, "Warnings", + message); + } + + protected void shouldGenerateWarning(CharSequence buggyCode, int line, + String message) { + shouldGenerateWarning(buggyCode, null, line, message); + } + + 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) { + Set<CompilationUnit> units = new HashSet<CompilationUnit>(); + addLongCheckingCups(units); + units.add(new MockCompilationUnit("Buggy", buggyCode.toString())); + if (extraCode != null) { + units.add(new MockCompilationUnit("Extra", extraCode.toString())); + } + return TypeOracleTestingUtils.buildStandardTypeOracleWith(logger, + units.toArray(new CompilationUnit[units.size()])); + } +} Modified: trunk/dev/core/test/com/google/gwt/dev/javac/JsniCheckerTest.java ============================================================================== --- trunk/dev/core/test/com/google/gwt/dev/javac/JsniCheckerTest.java (original) +++ trunk/dev/core/test/com/google/gwt/dev/javac/JsniCheckerTest.java Mon Jul 6 14:45:31 2009 @@ -15,20 +15,10 @@ */ package com.google.gwt.dev.javac; -import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.core.ext.TreeLogger.Type; -import com.google.gwt.core.ext.typeinfo.TypeOracle; -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 JsniCheckerTest extends TestCase { +public class JsniCheckerTest extends CheckerTestCase { /** * JSNI references to anonymous inner classes is deprecated. @@ -402,81 +392,5 @@ 2, "Referencing field 'Extra.Inner.x': type 'long' is not safe to access in JSNI 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) { - Set<CompilationUnit> units = new HashSet<CompilationUnit>(); - addLongCheckingCups(units); - units.add(new MockCompilationUnit("Buggy", buggyCode.toString())); - if (extraCode != null) { - units.add(new MockCompilationUnit("Extra", extraCode.toString())); - } - return TypeOracleTestingUtils.buildStandardTypeOracleWith(logger, - units.toArray(new CompilationUnit[units.size()])); - } - - private void shouldGenerate(CharSequence buggyCode, CharSequence extraCode, - int line, Type logLevel, String logHeader, String message) { - UnitTestTreeLogger.Builder b = new UnitTestTreeLogger.Builder(); - b.setLowestLogLevel(logLevel); - if (message != null) { - b.expect(logLevel, logHeader + " in '/mock/Buggy'", null); - final String fullMessage = "Line " + line + ": " + message; - b.expect(logLevel, fullMessage, null); - } - UnitTestTreeLogger logger = b.createLogger(); - TypeOracle oracle = buildOracle(buggyCode, extraCode, logger); - logger.assertCorrectLogEntries(); - if (message != null && logLevel == TreeLogger.ERROR) { - assertNull("Buggy compilation unit not removed from type oracle", - oracle.findType("Buggy")); - } else { - assertNotNull("Buggy compilation unit removed with only a warning", - oracle.findType("Buggy")); - } - } - - private void shouldGenerateError(CharSequence buggyCode, - CharSequence extraCode, int line, String message) { - shouldGenerate(buggyCode, extraCode, line, TreeLogger.ERROR, "Errors", - message); - } - - private void shouldGenerateError(CharSequence buggyCode, int line, - String message) { - shouldGenerateError(buggyCode, null, line, message); - } - - private void shouldGenerateNoError(CharSequence code) { - shouldGenerateNoError(code, null); - } - - private void shouldGenerateNoError(CharSequence code, CharSequence extraCode) { - shouldGenerateError(code, extraCode, -1, null); - } - - private void shouldGenerateNoWarning(CharSequence code) { - shouldGenerateWarning(code, -1, null); - } - - private void shouldGenerateWarning(CharSequence buggyCode, - CharSequence extraCode, int line, String message) { - shouldGenerate(buggyCode, extraCode, line, TreeLogger.WARN, "Warnings", - message); - } - - private void shouldGenerateWarning(CharSequence buggyCode, int line, - String message) { - shouldGenerateWarning(buggyCode, null, line, message); } } Modified: trunk/user/src/com/google/gwt/core/client/impl/Impl.java ============================================================================== --- trunk/user/src/com/google/gwt/core/client/impl/Impl.java (original) +++ trunk/user/src/com/google/gwt/core/client/impl/Impl.java Mon Jul 6 14:45:31 2009 @@ -79,7 +79,7 @@ * @return the name by which the named member can be accessed at runtime, or * <code>null</code> if the requested member has been pruned from the * output. - * @see com.google.gwt.core.client.ArtificialRescue + * @see com.google.gwt.core.client.impl.ArtificialRescue */ public static String getNameOf(String jsniIdent) { /* --~--~---------~--~----~------------~-------~--~----~ http://groups.google.com/group/Google-Web-Toolkit-Contributors -~----------~----~----~----~------~----~------~--~---
