}
@Override
public void endVisit(JMethodCall x, Context ctx) {
JMethod method = x.getTarget();
JsInvocation jsInvocation = new JsInvocation(x.getSourceInfo());
popList(jsInvocation.getArguments(), x.getArgs().size()); // args
if (JProgram.isClinit(method)) {
/*
* It is possible for clinits to be referenced here that have actually
* been retargeted (see {@link
* JTypeOracle.recomputeAfterOptimizations}). Most of the time, these
* will get cleaned up by other optimization passes prior to this point,
* but it's not guaranteed. In this case we need to replace the method
* call with the replaced clinit, unless the replacement is null, in
* which case we generate a JsNullLiteral as a place-holder expression.
*/
JDeclaredType type = method.getEnclosingType();
JDeclaredType clinitTarget = type.getClinitTarget();
if (clinitTarget == null || program.isJsTypePrototype(clinitTarget)) {
if (x.getInstance() != null) {
pop(); // instance
}
// generate a null expression, which will get optimized out
push(JsNullLiteral.INSTANCE);
return;
} else if (type != clinitTarget) {
// replace the method with its retargeted clinit
method = clinitTarget.getClinitMethod();
}
}
JsNameRef qualifier = null;
JsExpression unnecessaryQualifier = null;
JsExpression result = null;
boolean isJsProperty = false;
result = jsInvocation;
if (method.isStatic()) {
if (x.getInstance() != null) {
unnecessaryQualifier = pop(); // instance
}
qualifier = names.get(method).makeRef(x.getSourceInfo());
} else if (x.isStaticDispatchOnly() && method.isConstructor()) {
/*
* Constructor calls through {@code this} and {@code super} are always dispatched statically
* using the constructor function name (constructors are always defined as top level
* functions).
*
* Because constructors are modeled like instance methods they have an implicit {@code this}
* parameter, hence they are invoked like: "constructor.call(this, ...)".
*/
JsName callName = objectScope.declareName("call");
callName.setObfuscatable(false);
qualifier = callName.makeRef(x.getSourceInfo());
JsNameRef methodRef = names.get(method).makeRef(x.getSourceInfo());
qualifier.setQualifier(methodRef);
jsInvocation.getArguments().add(0, (JsExpression) pop()); // instance
if (program.isJsTypePrototype(method.getEnclosingType())) {
result = dispatchToSuperPrototype(x, method, qualifier, methodRef, jsInvocation);
}
} else if (x.isStaticDispatchOnly() && !method.isConstructor()) {
// Regular super call. This calls are always static and optimizations normally statify them.
// They can appear in completely unoptimized code, hence need to be handled here.
// Construct JCHSU.getPrototypeFor(type).polyname
// TODO(rluble): Ideally we would want to construct the inheritance chain the JS way and
// then we could do Type.prototype.polyname.call(this, ...). Currently prototypes do not
// have global names instead they are stuck into the prototypesByTypeId array.
final JDeclaredType superMethodTargetType = method.getEnclosingType();
JsInvocation getPrototypeCall = constructInvocation(x.getSourceInfo(),
"JavaClassHierarchySetupUtil.getClassPrototype",
JjsUtils.translateLiteral(program.getLiteral(typeMapper.get(superMethodTargetType))));
JsNameRef methodNameRef = polymorphicNames.get(method).makeRef(x.getSourceInfo());
methodNameRef.setQualifier(getPrototypeCall);