* The order of emulation is: - assign all synthetic fields from synthetic
* args - call our super constructor emulation method - call our instance
* initializer emulation method - run user code - return this
*/
void processConstructor(ConstructorDeclaration x) {
JMethod ctor = (JMethod) typeMap.get(x.binding);
try {
SourceInfo info = ctor.getSourceInfo();
currentMethod = ctor;
currentMethodBody = (JMethodBody) ctor.getBody();
currentMethodScope = x.scope;
JMethodCall superOrThisCall = null;
ExplicitConstructorCall ctorCall = x.constructorCall;
if (ctorCall != null) {
superOrThisCall = (JMethodCall) dispatch("processExpression",
ctorCall);
}
/*
* Determine if we have an explicit this call. The presence of an
* explicit this call indicates we can skip certain initialization steps
* (as the callee will perform those steps for us). These skippable
* steps are 1) assigning synthetic args to fields and 2) running
* initializers.
*/
boolean hasExplicitThis = (ctorCall != null)
&& !ctorCall.isSuperAccess();
JClassType enclosingType = (JClassType) ctor.getEnclosingType();
// Call clinit; $clinit is always in position 0.
JMethod clinitMethod = enclosingType.getMethods().get(0);
JMethodCall clinitCall = new JMethodCall(info, null, clinitMethod);
JMethodBody body = (JMethodBody) ctor.getBody();
JBlock block = body.getBlock();
block.addStmt(clinitCall.makeStatement());
/*
* All synthetic fields must be assigned, unless we have an explicit
* this constructor call, in which case the callee will assign them for
* us.
*/
if (!hasExplicitThis) {
ReferenceBinding declaringClass = x.binding.declaringClass;
if (declaringClass instanceof NestedTypeBinding) {
Iterator<JParameter> paramIt = getSyntheticsIterator();
NestedTypeBinding nestedBinding = (NestedTypeBinding) declaringClass;
if (nestedBinding.enclosingInstances != null) {
for (int i = 0; i < nestedBinding.enclosingInstances.length; ++i) {
SyntheticArgumentBinding arg = nestedBinding.enclosingInstances[i];
JParameter param = paramIt.next();
if (arg.matchingField != null) {
JField field = (JField) typeMap.get(arg);
block.addStmt(program.createAssignmentStmt(info,
createVariableRef(info, field), createVariableRef(info,
param)));
}
}
}
if (nestedBinding.outerLocalVariables != null) {
for (int i = 0; i < nestedBinding.outerLocalVariables.length; ++i) {
SyntheticArgumentBinding arg = nestedBinding.outerLocalVariables[i];
JParameter param = paramIt.next();
JField field = (JField) typeMap.get(arg);
block.addStmt(program.createAssignmentStmt(info,
createVariableRef(info, field), createVariableRef(info,
param)));
}
}
}
}
// Enums: wire up synthetic name/ordinal params to the super method.
if (enclosingType.isEnumOrSubclass() != null) {
assert (superOrThisCall != null);
JVariableRef enumNameRef = createVariableRef(
superOrThisCall.getSourceInfo(), ctor.getParams().get(0));
superOrThisCall.addArg(0, enumNameRef);
JVariableRef enumOrdinalRef = createVariableRef(
superOrThisCall.getSourceInfo(), ctor.getParams().get(1));
superOrThisCall.addArg(1, enumOrdinalRef);
}
// optional this or super constructor call
if (superOrThisCall != null) {
block.addStmt(superOrThisCall.makeStatement());
}
JExpression thisRef = createThisRef(info, enclosingType);
/*
* Call the synthetic instance initializer method, unless we have an
* explicit this constructor call, in which case the callee will.
*/
if (!hasExplicitThis) {
// $init is always in position 1 (clinit is in 0)
JMethod initMethod = enclosingType.getMethods().get(1);
JMethodCall initCall = new JMethodCall(info, thisRef, initMethod);
block.addStmt(initCall.makeStatement());
}
// user code (finally!)