blob: 6b594f75fab5fa4d960ec05eb6dfed430ac9eba4 [file] [log] [blame]
/*
* 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.jjs.impl;
import com.google.gwt.dev.javac.JsniCollector;
import com.google.gwt.dev.jdt.AbstractCompiler.CompilationResults;
import com.google.gwt.dev.jdt.SafeASTVisitor;
import com.google.gwt.dev.jjs.HasSourceInfo;
import com.google.gwt.dev.jjs.InternalCompilerException;
import com.google.gwt.dev.jjs.SourceInfo;
import com.google.gwt.dev.jjs.SourceOrigin;
import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JConstructor;
import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.jjs.ast.JEnumType;
import com.google.gwt.dev.jjs.ast.JField;
import com.google.gwt.dev.jjs.ast.JField.Disposition;
import com.google.gwt.dev.jjs.ast.JInterfaceType;
import com.google.gwt.dev.jjs.ast.JLocal;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JMethodBody;
import com.google.gwt.dev.jjs.ast.JNullType;
import com.google.gwt.dev.jjs.ast.JParameter;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JReferenceType;
import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.jjs.ast.js.JsniMethodBody;
import com.google.gwt.dev.js.JsAbstractSymbolResolver;
import com.google.gwt.dev.js.ast.JsFunction;
import com.google.gwt.dev.js.ast.JsName;
import com.google.gwt.dev.js.ast.JsNameRef;
import com.google.gwt.dev.js.ast.JsNode;
import com.google.gwt.dev.js.ast.JsParameter;
import com.google.gwt.dev.js.ast.JsProgram;
import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.AnnotationMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
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.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.NestedTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.SyntheticArgumentBinding;
import org.eclipse.jdt.internal.compiler.lookup.SyntheticMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.util.Util;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* This is a Builder for {@link TypeMap}. The whole point of this pass is to
* create raw unfinished, unlinked AST nodes for types, methods, fields, and
* parameters, and to map the original JDT nodes to these AST nodes. That way
* when GenerateJavaDom runs, it just uses the TypeMap output from this Builder
* to retrieve whatever referenceable nodes it needs without worrying about
* whether they need to be created. Building our AST from JDT starts here.
*/
public class BuildTypeMap {
/**
* Creates JNodes for every method, field, initializer, parameter, and local
* and memorizes the mapping from the JDT Binding to the corresponding JNode
* for each thing created. Note that this pass also 1) sets up the super
* type(s) for any member or local types created in BuildTypeMapVisitor (see
* the comments there about why it had to be deferred). 2) adds each
* user-defined type to a flat list. 3) Creates JNodes for all methods and
* variables and memorizes the mapping from the JDT Binding to the
* corresponding JNode for each created method and variable. 4) Maps all
* synthetic arguments and fields for nested and local classes. 5) Slurps in
* JSNI code for native methods as an opaque string.
*
* Note that methods and fields are not added to their classes here, that
* isn't done until {@link GenerateJavaAST}.
*/
private class BuildDeclMapVisitor extends SafeASTVisitor {
private String currentFileName;
private int[] currentSeparatorPositions;
private final List<TypeDeclaration> typeDecls = new ArrayList<TypeDeclaration>();
public TypeDeclaration[] getTypeDeclarataions() {
return typeDecls.toArray(new TypeDeclaration[typeDecls.size()]);
}
@Override
public boolean visit(AnnotationMethodDeclaration methodDeclaration, ClassScope scope) {
return visit((MethodDeclaration) methodDeclaration, scope);
}
@Override
public boolean visit(Argument argument, BlockScope scope) {
try {
if (scope == scope.methodScope()) {
return true;
}
JMethodBody enclosingBody = findEnclosingMethod(scope);
if (enclosingBody == null) {
// Happens in the case of external types
return true;
}
SourceInfo info = makeSourceInfo(argument, enclosingBody.getMethod());
LocalVariableBinding b = argument.binding;
JType localType = getType(b.type);
JLocal newLocal =
JProgram.createLocal(info, String.valueOf(argument.name), localType, b.isFinal(),
enclosingBody);
typeMap.put(b, newLocal);
return true;
} catch (Throwable e) {
throw translateException(argument, e);
}
}
@Override
public boolean visit(ConstructorDeclaration ctorDecl, ClassScope scope) {
try {
MethodBinding b = ctorDecl.binding;
JClassType enclosingType = (JClassType) getType(scope.enclosingSourceType());
SourceInfo info = makeSourceInfo(ctorDecl, enclosingType);
processConstructor(b, ctorDecl, info);
return true;
} catch (Throwable e) {
throw translateException(ctorDecl, e);
}
}
@Override
public boolean visit(FieldDeclaration fieldDeclaration, MethodScope scope) {
try {
FieldBinding b = fieldDeclaration.binding;
JDeclaredType enclosingType = (JDeclaredType) getType(scope.enclosingSourceType());
SourceInfo info = makeSourceInfo(fieldDeclaration, enclosingType);
Expression initialization = fieldDeclaration.initialization;
if (initialization != null && initialization instanceof AllocationExpression
&& ((AllocationExpression) initialization).enumConstant != null) {
createEnumField(info, b, enclosingType);
} else {
createField(info, b, enclosingType);
}
return true;
} catch (Throwable e) {
throw translateException(fieldDeclaration, e);
}
}
@Override
public boolean visit(LocalDeclaration localDeclaration, BlockScope scope) {
try {
LocalVariableBinding b = localDeclaration.binding;
TypeBinding resolvedType = localDeclaration.type.resolvedType;
JType localType;
if (resolvedType.constantPoolName() != null) {
localType = getType(resolvedType);
} else {
// Special case, a statically unreachable local type.
localType = JNullType.INSTANCE;
}
JMethodBody enclosingBody = findEnclosingMethod(scope);
if (enclosingBody == null) {
// Happens in the case of external types
return true;
}
SourceInfo info = makeSourceInfo(localDeclaration, enclosingBody.getMethod());
JLocal newLocal =
JProgram.createLocal(info, String.valueOf(localDeclaration.name), localType, b
.isFinal(), enclosingBody);
typeMap.put(b, newLocal);
return true;
} catch (Throwable e) {
throw translateException(localDeclaration, e);
}
}
@Override
public boolean visit(MethodDeclaration methodDeclaration, ClassScope scope) {
try {
MethodBinding b = methodDeclaration.binding;
JDeclaredType enclosingType = (JDeclaredType) getType(scope.enclosingSourceType());
SourceInfo info = makeSourceInfo(methodDeclaration, enclosingType);
JMethod newMethod = processMethodBinding(b, enclosingType, info);
SourceInfo methodInfo = makeSourceInfo(methodDeclaration, newMethod);
mapParameters(newMethod, methodDeclaration, methodInfo);
info.addCorrelation(info.getCorrelator().by(newMethod));
if (newMethod.isNative()) {
processNativeMethod(methodDeclaration, info, enclosingType, newMethod);
}
return true;
} catch (Throwable e) {
throw translateException(methodDeclaration, e);
}
}
@Override
public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
return process(memberTypeDeclaration);
}
@Override
public boolean visit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
return process(typeDeclaration);
}
@Override
public boolean visitValid(TypeDeclaration localTypeDeclaration, BlockScope scope) {
return process(localTypeDeclaration);
}
private JField createEnumField(SourceInfo info, FieldBinding binding,
JReferenceType enclosingType) {
JType type = getType(binding.type);
JField field =
program.createEnumField(info, String.valueOf(binding.name), (JEnumType) enclosingType,
(JClassType) type, binding.original().id);
info.addCorrelation(info.getCorrelator().by(field));
typeMap.put(binding, field);
return field;
}
private JMethodBody findEnclosingMethod(BlockScope scope) {
JMethod method;
MethodScope methodScope = scope.methodScope();
if (methodScope.isInsideInitializer()) {
JDeclaredType enclosingType =
(JDeclaredType) getType(scope.classScope().referenceContext.binding);
if (methodScope.isStatic) {
// clinit
method = enclosingType.getMethods().get(0);
} else {
// init
assert (enclosingType instanceof JClassType);
method = enclosingType.getMethods().get(1);
}
} else {
AbstractMethodDeclaration referenceMethod = methodScope.referenceMethod();
method = (JMethod) typeMap.get(referenceMethod.binding);
}
assert !method.isNative() && !method.isAbstract();
return (JMethodBody) (method.isExternal() ? null : method.getBody());
}
private SourceInfo makeSourceInfo(AbstractMethodDeclaration methodDecl, HasSourceInfo enclosing) {
int startLine =
Util.getLineNumber(methodDecl.sourceStart, currentSeparatorPositions, 0,
currentSeparatorPositions.length - 1);
SourceOrigin toReturn =
SourceOrigin.create(methodDecl.sourceStart, methodDecl.bodyEnd, startLine,
currentFileName);
if (enclosing != null) {
return enclosing.getSourceInfo().makeChild(toReturn);
}
return toReturn;
}
private SourceInfo makeSourceInfo(Statement stmt, HasSourceInfo enclosing) {
int startLine =
Util.getLineNumber(stmt.sourceStart, currentSeparatorPositions, 0,
currentSeparatorPositions.length - 1);
SourceOrigin toReturn =
SourceOrigin.create(stmt.sourceStart, stmt.sourceEnd, startLine, currentFileName);
if (enclosing != null) {
return enclosing.getSourceInfo().makeChild(toReturn);
}
return toReturn;
}
/**
* Add synthetic fields, setup super types. You'll notice that we DON'T have
* any concept of "inner" or "outer" types in our AST. Truth is, we found it
* easier to simply model everything as flat classes and emulate the nesting
* behavior (and final local access on local classes). It's much closer to
* how we'll eventually be generating JavaScript code (code generation is
* more straightforward), it makes for fewer kinds of things to worry about
* when optimizing (more aggressive optimizations), and it's how Java
* actually implements the stuff under the hood anyway.
*/
private boolean process(TypeDeclaration typeDeclaration) {
CompilationResult compResult = typeDeclaration.compilationResult;
currentSeparatorPositions = compResult.lineSeparatorPositions;
currentFileName = String.valueOf(compResult.fileName);
if (BuildTypeMap.this.process(typeDeclaration.binding)) {
if (!linker.isExternalType(dotify(typeDeclaration.binding.compoundName))) {
typeDecls.add(typeDeclaration);
}
return true;
}
return false;
}
private void processNativeMethod(MethodDeclaration methodDeclaration, SourceInfo info,
JDeclaredType enclosingType, JMethod newMethod) {
// TODO: use existing parsed JSNI functions from CompilationState.
// Handle JSNI block
char[] source = methodDeclaration.compilationResult().getCompilationUnit().getContents();
String unitSource = String.valueOf(source);
JsFunction jsFunction =
JsniCollector.parseJsniFunction(methodDeclaration, unitSource, enclosingType.getName(),
info, jsProgram.getScope());
if (jsFunction != null) {
jsFunction.setFromJava(true);
((JsniMethodBody) newMethod.getBody()).setFunc(jsFunction);
// Ensure that we've resolved the parameter and local references within
// the JSNI method for later pruning.
JsParameterResolver localResolver = new JsParameterResolver(jsFunction);
localResolver.accept(jsFunction);
}
}
private InternalCompilerException translateException(AbstractMethodDeclaration amd, Throwable e) {
if (e instanceof VirtualMachineError) {
// Always rethrow VM errors (an attempt to wrap may fail).
throw (VirtualMachineError) e;
}
InternalCompilerException ice;
if (e instanceof InternalCompilerException) {
ice = (InternalCompilerException) e;
} else {
ice = new InternalCompilerException("Error building type map", e);
}
ice.addNode(amd.getClass().getName(), amd.toString(), makeSourceInfo(amd, null));
return ice;
}
private InternalCompilerException translateException(Statement stmt, Throwable e) {
if (e instanceof VirtualMachineError) {
// Always rethrow VM errors (an attempt to wrap may fail).
throw (VirtualMachineError) e;
}
InternalCompilerException ice;
if (e instanceof InternalCompilerException) {
ice = (InternalCompilerException) e;
} else {
ice = new InternalCompilerException("Error building type map", e);
}
ice.addNode(stmt.getClass().getName(), stmt.toString(), makeSourceInfo(stmt, null));
return ice;
}
}
/**
* Creates JNodes for every type and memorizes the mapping from the JDT
* Binding to the corresponding JNode for each created type. Note that since
* there could be forward references, it is not possible to set up super
* types; it must be done is a subsequent pass.
*/
private class BuildTypeMapVisitor extends SafeASTVisitor {
@Override
public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
return process(memberTypeDeclaration);
}
@Override
public boolean visit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
return process(typeDeclaration);
}
@Override
public boolean visitValid(TypeDeclaration localTypeDeclaration, BlockScope scope) {
assert (TypeDeclaration.kind(localTypeDeclaration.modifiers) != TypeDeclaration.INTERFACE_DECL);
return process(localTypeDeclaration);
}
private SourceInfo makeSourceInfo(TypeDeclaration typeDecl) {
CompilationResult compResult = typeDecl.compilationResult;
int[] indexes = compResult.lineSeparatorPositions;
String fileName = String.valueOf(compResult.fileName);
int startLine = Util.getLineNumber(typeDecl.sourceStart, indexes, 0, indexes.length - 1);
return program.createSourceInfo(typeDecl.sourceStart, typeDecl.bodyEnd, startLine, fileName);
}
private boolean process(TypeDeclaration typeDeclaration) {
try {
SourceTypeBinding binding = typeDeclaration.binding;
String name = dotify(binding.compoundName);
if (binding instanceof LocalTypeBinding) {
char[] localName = binding.constantPoolName();
name = new String(localName).replace('/', '.');
}
SourceInfo info = makeSourceInfo(typeDeclaration);
typeMap.put(binding, createType(name, info, binding));
return true;
} catch (Throwable e) {
throw translateException(typeDeclaration, e);
}
}
private InternalCompilerException translateException(TypeDeclaration typeDecl, Throwable e) {
if (e instanceof VirtualMachineError) {
// Always rethrow VM errors (an attempt to wrap may fail).
throw (VirtualMachineError) e;
}
InternalCompilerException ice;
if (e instanceof InternalCompilerException) {
ice = (InternalCompilerException) e;
} else {
ice = new InternalCompilerException("Error building type map", e);
}
ice.addNode(typeDecl.getClass().getName(), typeDecl.toString(), makeSourceInfo(typeDecl));
return ice;
}
}
private class ExternalTypeCreator implements ExternalTypeTask {
public void process(String klass, BinaryTypeBinding binding) {
if (program.getFromTypeMap(klass) == null) {
// NB(tobyr) There are a few cases where certain compiler intrinsic
// types are only needed if the program references them
// (e.g. boxed numeric types). If we don't have the binding for those
// types, we can safely ignore it.
createExternalType(klass, binding);
}
}
}
private class ExternalTypeResolver implements ExternalTypeTask {
public void process(String klass, BinaryTypeBinding binding) {
if (binding != null) {
JDeclaredType type = program.getFromTypeMap(klass);
resolve(type, binding);
}
}
}
private interface ExternalTypeTask {
void process(String klass, BinaryTypeBinding binding);
}
/**
* Resolves the scope of JS identifiers solely within the scope of a method.
*/
private static class JsParameterResolver extends JsAbstractSymbolResolver {
private final JsFunction jsFunction;
public JsParameterResolver(JsFunction jsFunction) {
this.jsFunction = jsFunction;
}
@Override
public void resolve(JsNameRef x) {
// Only resolve unqualified names
if (x.getQualifier() == null) {
JsName name = getScope().findExistingName(x.getIdent());
// Ensure that we're resolving a name from the function's parameters
JsNode node = name == null ? null : name.getStaticRef();
if (node instanceof JsParameter) {
JsParameter param = (JsParameter) node;
if (jsFunction.getParameters().contains(param)) {
x.resolve(name);
}
}
}
}
}
// TODO: Remove this overload altogether at some point.
public static TypeDeclaration[] exec(TypeMap typeMap, CompilationResults results,
JsProgram jsProgram, TypeLinker linker) {
BuildTypeMap btm = new BuildTypeMap(typeMap, jsProgram, linker, results.compiledUnits);
Event buildTypeMapEvent = SpeedTracerLogger.start(CompilerEventType.BUILD_TYPE_MAP_FOR_AST);
btm.createPeersForUnits();
btm.resolveExternalTypes(results.binaryBindings);
TypeDeclaration[] result = btm.createPeersForNonTypeDecls();
buildTypeMapEvent.end();
return result;
}
public static TypeDeclaration[] exec(TypeMap typeMap, CompilationUnitDeclaration[] unitDecls,
JsProgram jsProgram) {
CompilationResults results =
new CompilationResults(unitDecls, new HashMap<String, BinaryTypeBinding>(0));
return exec(typeMap, results, jsProgram, TypeLinker.NULL_TYPE_LINKER);
}
static String dotify(char[][] name) {
StringBuffer result = new StringBuffer();
for (int i = 0; i < name.length; ++i) {
if (i > 0) {
result.append('.');
}
result.append(name[i]);
}
return result.toString();
}
private final JsProgram jsProgram;
private final TypeLinker linker;
private final JProgram program;
private final TypeMap typeMap;
private final CompilationUnitDeclaration[] unitDecls;
private BuildTypeMap(TypeMap typeMap, JsProgram jsProgram, TypeLinker linker,
CompilationUnitDeclaration[] unitDecls) {
this.typeMap = typeMap;
this.program = typeMap.getProgram();
this.jsProgram = jsProgram;
this.linker = linker;
this.unitDecls = unitDecls;
}
private void addThrownExceptions(MethodBinding methodBinding, JMethod method) {
for (ReferenceBinding thrownBinding : methodBinding.thrownExceptions) {
JClassType type = (JClassType) getType(thrownBinding.erasure());
method.addThrownException(type);
}
}
private JDeclaredType createExternalType(String name, ReferenceBinding binding) {
char[] chars = binding.getFileName();
String fileName = chars == null ? "" : String.valueOf(chars);
SourceInfo sourceInfo = SourceOrigin.create(0, fileName);
JDeclaredType type = createType(name, sourceInfo, binding);
typeMap.put(binding, type);
return type;
}
private JField createField(SourceInfo info, FieldBinding binding, JDeclaredType enclosingType) {
JType type = getType(binding.type);
JField field =
program.createField(info, String.valueOf(binding.name), enclosingType, type, binding
.isStatic(), GwtAstBuilder.getFieldDisposition(binding));
typeMap.put(binding, field);
info.addCorrelation(info.getCorrelator().by(field));
return field;
}
private JField createField(SyntheticArgumentBinding binding, JDeclaredType enclosingType,
Disposition disposition) {
JType type = getType(binding.type);
SourceInfo info = enclosingType.getSourceInfo().makeChild();
JField field =
program.createField(info, String.valueOf(binding.name), enclosingType, type, false,
disposition);
info.addCorrelation(info.getCorrelator().by(field));
if (binding.matchingField != null) {
typeMap.put(binding.matchingField, field);
}
typeMap.put(binding, field);
return field;
}
private void createMethod(MethodBinding binding, SourceInfo info) {
JDeclaredType enclosingType = (JDeclaredType) getType(binding.declaringClass);
JMethod newMethod = processMethodBinding(binding, enclosingType, info);
mapParameters(newMethod, binding, info);
}
private JParameter createParameter(LocalVariableBinding binding, JMethod enclosingMethod,
SourceInfo info) {
JType type = getType(binding.type);
JParameter param =
JProgram.createParameter(info, String.valueOf(binding.name), type, binding.isFinal(),
false, enclosingMethod);
typeMap.put(binding, param);
return param;
}
private JParameter createParameter(SyntheticArgumentBinding arg, String argName,
JMethod enclosingMethod) {
JType type = getType(arg.type);
JParameter param =
JProgram.createParameter(enclosingMethod.getSourceInfo(), argName, type, true, false,
enclosingMethod);
return param;
}
private JParameter createParameter(TypeBinding paramType, JMethod enclosingMethod,
SourceInfo info, int argPosition) {
JType type = getType(paramType);
// TODO(tobyr) Get the actual param name if it's present in debug info
// or otherwise.
String syntheticParamName = "arg" + argPosition;
JParameter param =
JProgram.createParameter(info, syntheticParamName, type, true, false, enclosingMethod);
// Don't need to put the parameter in the TypeMap as it won't be looked
// up for binary types.
return param;
}
private TypeDeclaration[] createPeersForNonTypeDecls() {
// Traverse to create our JNode peers for each method, field,
// parameter, and local
BuildDeclMapVisitor v = new BuildDeclMapVisitor();
for (CompilationUnitDeclaration unitDecl : unitDecls) {
unitDecl.traverse(v, unitDecl.scope);
}
return v.getTypeDeclarataions();
}
private void createPeersForUnits() {
// Traverse to create our JNode peers for each type
BuildTypeMapVisitor v = new BuildTypeMapVisitor();
for (CompilationUnitDeclaration unitDecl : unitDecls) {
unitDecl.traverse(v, unitDecl.scope);
}
}
private JDeclaredType createType(String name, SourceInfo info, ReferenceBinding binding) {
JDeclaredType newType;
if (binding.isClass()) {
newType = program.createClass(info, name, binding.isAbstract(), binding.isFinal());
} else if (binding.isInterface() || binding.isAnnotationType()) {
newType = program.createInterface(info, name);
} else if (binding.isEnum()) {
if (binding.isAnonymousType()) {
// Don't model an enum subclass as a JEnumType.
newType = program.createClass(info, name, false, true);
} else {
newType = program.createEnum(info, name, binding.isAbstract());
}
} else {
throw new InternalCompilerException("ReferenceBinding is not a class, interface, or enum.");
}
info.addCorrelation(info.getCorrelator().by(newType));
/**
* We emulate static initializers and instance initializers as methods. As
* in other cases, this gives us: simpler AST, easier to optimize, more like
* output JavaScript. Clinit is always in slot 0, init (if it exists) is
* always in slot 1.
*/
SourceInfo child = info.makeChild();
JMethod clinit =
program.createMethod(child, "$clinit", newType, program.getTypeVoid(), false, true, true,
true, false);
clinit.freezeParamTypes();
clinit.setSynthetic();
child.addCorrelation(info.getCorrelator().by(clinit));
if (newType instanceof JClassType) {
child = info.makeChild();
JMethod init =
program.createMethod(child, "$init", newType, program.getTypeVoid(), false, false, true,
true, false);
init.freezeParamTypes();
init.setSynthetic();
child.addCorrelation(info.getCorrelator().by(init));
}
newType.setExternal(linker.isExternalType(newType.getName()));
return newType;
}
private void forEachExternalType(Map<String, BinaryTypeBinding> bindings, ExternalTypeTask task) {
for (Map.Entry<String, BinaryTypeBinding> entry : bindings.entrySet()) {
String klass = entry.getKey();
if (linker.isExternalType(klass)) {
BinaryTypeBinding binding = bindings.get(klass);
if (binding != null) {
task.process(klass, binding);
}
}
}
}
private JType getType(TypeBinding binding) {
JType type = (JType) typeMap.tryGet(binding);
if (type != null) {
return type;
}
if (binding instanceof ArrayBinding) {
binding = ((ArrayBinding) binding).leafComponentType;
}
if (!(binding instanceof ReferenceBinding)) {
throw new InternalCompilerException("Expected a ReferenceBinding but received a "
+ binding.getClass());
}
ReferenceBinding refBinding = (ReferenceBinding) binding;
String name = dotify(refBinding.compoundName);
if (!linker.isExternalType(name)) {
// typeMap.get() will fail with an appropriate exception
return (JType) typeMap.get(binding);
}
type = createExternalType(name, refBinding);
resolve((JDeclaredType) type, refBinding);
return type;
}
private void mapParameters(JMethod method, AbstractMethodDeclaration x, SourceInfo info) {
MethodBinding b = x.binding;
int paramCount = (b.parameters != null ? b.parameters.length : 0);
if (paramCount > 0) {
for (int i = 0, n = x.arguments.length; i < n; ++i) {
createParameter(x.arguments[i].binding, method, info);
}
}
method.freezeParamTypes();
}
private void mapParameters(JMethod method, MethodBinding binding, SourceInfo info) {
int paramCount = binding.parameters != null ? binding.parameters.length : 0;
if (paramCount > 0) {
int counter = 0;
for (TypeBinding argType : binding.parameters) {
createParameter(argType, method, info, counter++);
}
}
method.freezeParamTypes();
}
private boolean process(ReferenceBinding binding) {
JDeclaredType type = (JDeclaredType) getType(binding);
try {
// Create an override for getClass().
if (type instanceof JClassType && type != program.getTypeJavaLangObject()) {
SourceInfo info = type.getSourceInfo().makeChild();
JMethod getClassMethod =
program.createMethod(info, "getClass", type, program.getTypeJavaLangClass(), false,
false, false, false, false);
assert (type.getMethods().get(2) == getClassMethod);
getClassMethod.freezeParamTypes();
getClassMethod.setSynthetic();
info.addCorrelation(info.getCorrelator().by(getClassMethod));
}
if (binding.isNestedType() && !binding.isStatic() && !(binding instanceof BinaryTypeBinding)) {
// TODO(tobyr) Do something here for binary types?
// add synthetic fields for outer this and locals
assert (type instanceof JClassType);
NestedTypeBinding nestedBinding = (NestedTypeBinding) binding;
if (nestedBinding.enclosingInstances != null) {
for (int i = 0; i < nestedBinding.enclosingInstances.length; ++i) {
SyntheticArgumentBinding arg = nestedBinding.enclosingInstances[i];
createField(arg, type, Disposition.THIS_REF);
}
}
if (nestedBinding.outerLocalVariables != null) {
for (int i = 0; i < nestedBinding.outerLocalVariables.length; ++i) {
SyntheticArgumentBinding arg = nestedBinding.outerLocalVariables[i];
// See InnerClassTest.testOuterThisFromSuperCall().
boolean isReallyThisRef = false;
if (arg.actualOuterLocalVariable instanceof SyntheticArgumentBinding) {
SyntheticArgumentBinding outer =
(SyntheticArgumentBinding) arg.actualOuterLocalVariable;
if (outer.matchingField != null) {
JField field = (JField) typeMap.get(outer.matchingField);
if (field.isThisRef()) {
isReallyThisRef = true;
}
}
}
createField(arg, type, isReallyThisRef ? Disposition.THIS_REF : Disposition.FINAL);
}
}
}
ReferenceBinding superClassBinding = binding.superclass();
if (type instanceof JClassType && superClassBinding != null) {
// TODO: handle separately?
assert (binding.superclass().isClass() || binding.superclass().isEnum());
JClassType superClass = (JClassType) getType(superClassBinding);
((JClassType) type).setSuperClass(superClass);
}
ReferenceBinding[] superInterfaces = binding.superInterfaces();
for (ReferenceBinding superInterfaceBinding : superInterfaces) {
assert (superInterfaceBinding.isInterface());
JInterfaceType superInterface = (JInterfaceType) getType(superInterfaceBinding);
type.addImplements(superInterface);
}
ReferenceBinding enclosingBinding = binding.enclosingType();
if (enclosingBinding != null) {
type.setEnclosingType((JDeclaredType) getType(enclosingBinding));
}
if (type instanceof JEnumType) {
processEnumType(binding, (JEnumType) type);
}
return true;
} catch (VirtualMachineError e) {
// Always rethrow VM errors (an attempt to wrap may fail).
throw e;
} catch (InternalCompilerException ice) {
ice.addNode(type);
throw ice;
} catch (Throwable e) {
throw new InternalCompilerException(type, "Error building type map", e);
}
}
private JConstructor processConstructor(MethodBinding b, ConstructorDeclaration decl,
SourceInfo info) {
JClassType enclosingType = (JClassType) getType(b.declaringClass);
JConstructor newCtor = program.createConstructor(info, enclosingType);
// Enums have hidden arguments for name and value
if (enclosingType.isEnumOrSubclass() != null) {
JProgram.createParameter(info, "enum$name", program.getTypeJavaLangString(), true, false,
newCtor);
JProgram.createParameter(info, "enum$ordinal", program.getTypePrimitiveInt(), true, false,
newCtor);
}
ReferenceBinding declaringClass = b.declaringClass;
Set<String> alreadyNamedVariables = new HashSet<String>();
if (declaringClass.isNestedType() && !declaringClass.isStatic()) {
// TODO(tobyr) Do we have to do the equivalent for binary types here
// or will this just fall out correctly?
// add synthetic args for outer this
NestedTypeBinding nestedBinding = (NestedTypeBinding) declaringClass;
if (nestedBinding.enclosingInstances != null) {
for (int i = 0; i < nestedBinding.enclosingInstances.length; ++i) {
SyntheticArgumentBinding arg = nestedBinding.enclosingInstances[i];
String argName = String.valueOf(arg.name);
if (alreadyNamedVariables.contains(argName)) {
argName += "_" + i;
}
createParameter(arg, argName, newCtor);
alreadyNamedVariables.add(argName);
}
}
}
// user args
if (decl == null) {
mapParameters(newCtor, b, info);
} else {
mapParameters(newCtor, decl, info);
}
// original params are now frozen
addThrownExceptions(b, newCtor);
info.addCorrelation(info.getCorrelator().by(newCtor));
if (declaringClass.isNestedType() && !declaringClass.isStatic()) {
// add synthetic args for locals
NestedTypeBinding nestedBinding = (NestedTypeBinding) declaringClass;
// add synthetic args for outer this and locals
if (nestedBinding.outerLocalVariables != null) {
for (int i = 0; i < nestedBinding.outerLocalVariables.length; ++i) {
SyntheticArgumentBinding arg = nestedBinding.outerLocalVariables[i];
String argName = String.valueOf(arg.name);
if (alreadyNamedVariables.contains(argName)) {
argName += "_" + i;
}
createParameter(arg, argName, newCtor);
alreadyNamedVariables.add(argName);
}
}
}
if (enclosingType.isExternal()) {
newCtor.setBody(null);
}
typeMap.put(b, newCtor);
return newCtor;
}
private void processEnumType(ReferenceBinding binding, JEnumType type) {
// Visit the synthetic values() and valueOf() methods.
for (MethodBinding methodBinding : binding.methods()) {
if (methodBinding instanceof SyntheticMethodBinding) {
JMethod newMethod = processMethodBinding(methodBinding, type, type.getSourceInfo());
TypeBinding[] parameters = methodBinding.parameters;
if (parameters.length == 0) {
assert newMethod.getName().equals("values");
} else if (parameters.length == 1) {
assert newMethod.getName().equals("valueOf");
assert typeMap.get(parameters[0]) == program.getTypeJavaLangString();
JProgram.createParameter(newMethod.getSourceInfo(), "name", program
.getTypeJavaLangString(), true, false, newMethod);
} else {
assert false;
}
newMethod.freezeParamTypes();
}
}
}
private void processExternalMethod(MethodBinding binding, JDeclaredType type) {
if (binding.isPrivate()
|| (type.getName().startsWith("java.") && !binding.isPublic() && !binding.isProtected())) {
return;
}
if (binding.isConstructor()) {
processConstructor(binding, null, type.getSourceInfo());
} else {
createMethod(binding, type.getSourceInfo());
}
}
private JMethod processMethodBinding(MethodBinding b, JDeclaredType enclosingType, SourceInfo info) {
JType returnType = getType(b.returnType);
JMethod newMethod =
program.createMethod(info, String.valueOf(b.selector), enclosingType, returnType, b
.isAbstract(), b.isStatic(), b.isFinal(), b.isPrivate(), b.isNative());
addThrownExceptions(b, newMethod);
if (b.isSynthetic()) {
newMethod.setSynthetic();
}
if (enclosingType.isExternal()) {
newMethod.setBody(null);
}
typeMap.put(b, newMethod);
return newMethod;
}
private void resolve(JDeclaredType type, ReferenceBinding binding) {
process(binding);
for (FieldBinding fieldBinding : binding.fields()) {
if (fieldBinding.isPrivate()
|| (type.getName().startsWith("java.") && !fieldBinding.isPublic() && !fieldBinding
.isProtected())) {
continue;
}
createField(type.getSourceInfo(), fieldBinding, type);
}
for (MethodBinding methodBinding : binding.methods()) {
processExternalMethod(methodBinding, type);
}
if (binding instanceof BinaryTypeBinding) {
// Unlike SourceTypeBindings, we have to explicitly ask for bridge methods
// for BinaryTypeBindings.
try {
// TODO(tobyr) Fix so we don't have to use reflection.
Method m = BinaryTypeBinding.class.getDeclaredMethod("bridgeMethods");
MethodBinding[] bridgeMethods = (MethodBinding[]) m.invoke(binding);
for (MethodBinding methodBinding : bridgeMethods) {
processExternalMethod(methodBinding, type);
}
} catch (Exception e) {
throw new InternalCompilerException("Unexpected failure", e);
}
}
}
/**
* Creates and resolves all external types.
*/
private void resolveExternalTypes(Map<String, BinaryTypeBinding> bindings) {
forEachExternalType(bindings, new ExternalTypeCreator());
forEachExternalType(bindings, new ExternalTypeResolver());
}
}