All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.dev.jjs.impl.JsoDevirtualizer Maven / Gradle / Ivy

There is a newer version: 2.10.0
Show newest version
/*
 * 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.jjs.SourceInfo;
import com.google.gwt.dev.jjs.SourceOrigin;
import com.google.gwt.dev.jjs.ast.AccessModifier;
import com.google.gwt.dev.jjs.ast.Context;
import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JConditional;
import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.jjs.ast.JLocal;
import com.google.gwt.dev.jjs.ast.JLocalRef;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JMethodBody;
import com.google.gwt.dev.jjs.ast.JMethodCall;
import com.google.gwt.dev.jjs.ast.JModVisitor;
import com.google.gwt.dev.jjs.ast.JParameter;
import com.google.gwt.dev.jjs.ast.JParameterRef;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JReturnStatement;
import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.jjs.ast.JTypeOracle;
import com.google.gwt.dev.jjs.ast.js.JMultiExpression;
import com.google.gwt.dev.jjs.impl.MakeCallsStatic.CreateStaticImplsVisitor;

import java.util.HashMap;
import java.util.Map;

/**
 * All call sites that might result in virtual dispatch to a JSO must be
 * rewritten to force static dispatch. This transform may NOT be run multiple
 * times; it will create ever-expanding replacement expressions.
 */
public class JsoDevirtualizer {

  /**
   * Rewrite any virtual dispatches to Object or JavaScriptObject such that
   * dispatch occurs statically for JSOs.
   */
  private class RewriteVirtualDispatches extends JModVisitor {

    /**
     * A method call at this point can be one of 5 things:
     * 
    *
  1. a dual dispatch interface
  2. *
  3. a single dispatch trough single-jso interface
  4. *
  5. a java.lang.Object override from JavaScriptObject
  6. *
  7. a regular dispatch (no JSOs involved or static JSO call)
  8. *
  9. in draftMode, a 'static' virtual JSO call that hasn't been made * static yet.
  10. *
*/ @Override public void endVisit(JMethodCall x, Context ctx) { JMethod method = x.getTarget(); JMethod newMethod; JDeclaredType targetType = method.getEnclosingType(); if (targetType == null) { return; } if (!method.needsVtable()) { return; } JType instanceType = x.getInstance().getType(); // if the instance can't possibly be a JSO, don't devirtualize if (instanceType != program.getTypeJavaLangObject() && !program.typeOracle.canBeJavaScriptObject(instanceType)) { return; } if (polyMethodToJsoMethod.containsKey(method)) { // already did this one before newMethod = polyMethodToJsoMethod.get(method); } else if (program.typeOracle.isDualJsoInterface(targetType)) { JMethod overridingMethod = findOverridingMethod(method, program.typeOracle.getSingleJsoImpl(targetType)); assert overridingMethod != null; JMethod jsoStaticImpl = getStaticImpl(overridingMethod); newMethod = getOrCreateDevirtualMethod(x, jsoStaticImpl); polyMethodToJsoMethod.put(method, newMethod); } else if (program.isJavaScriptObject(targetType)) { // It's a virtual JSO dispatch, usually occurs in draftCompile newMethod = getStaticImpl(method); polyMethodToJsoMethod.put(method, newMethod); } else if (program.typeOracle.isSingleJsoImpl(targetType)) { // interface dispatch with single implementing JSO concrete type JMethod overridingMethod = findOverridingMethod(method, program.typeOracle.getSingleJsoImpl(targetType)); assert overridingMethod != null; newMethod = getStaticImpl(overridingMethod); polyMethodToJsoMethod.put(method, newMethod); } else if (targetType == program.getTypeJavaLangObject()) { // it's a java.lang.Object overriden method in JSO JMethod overridingMethod = findOverridingMethod(method, program.getJavaScriptObject()); if (overridingMethod != null) { JMethod jsoStaticImpl = getStaticImpl(overridingMethod); newMethod = getOrCreateDevirtualMethod(x, jsoStaticImpl); polyMethodToJsoMethod.put(method, newMethod); } else { // else this method isn't overriden by JavaScriptObject assert false : "Object method not overriden by JavaScriptObject"; return; } } else { return; } assert (newMethod != null); ctx.replaceMe(MakeCallsStatic.makeStaticCall(x, newMethod)); } @Override public boolean visit(JMethod x, Context ctx) { // Don't rewrite the polymorphic call inside of the devirtualizing method! if (polyMethodToDevirtualMethods.containsValue(x)) { return false; } return true; } } public static void exec(JProgram program) { new JsoDevirtualizer(program).execImpl(); } /** * Maps each Object instance methods (ie, {@link Object#equals(Object)}) onto * its corresponding devirtualizing method. */ protected Map polyMethodToJsoMethod = new HashMap(); /** * Contains the Cast.isJavaObject method. */ private final JMethod isJavaObjectMethod; /** * Key is the method signature, value is the number of unique instances with * the same signature. */ private Map jsoMethodInstances = new HashMap(); /** * Contains the set of devirtualizing methods that replace polymorphic calls * to Object methods. */ private final Map polyMethodToDevirtualMethods = new HashMap(); private final JProgram program; private final CreateStaticImplsVisitor staticImplCreator; private JsoDevirtualizer(JProgram program) { this.program = program; this.isJavaObjectMethod = program.getIndexedMethod("Cast.isJavaObject"); staticImplCreator = new CreateStaticImplsVisitor(program); } private void execImpl() { JClassType jsoType = program.getJavaScriptObject(); if (jsoType == null) { return; } RewriteVirtualDispatches rewriter = new RewriteVirtualDispatches(); rewriter.accept(program); assert (rewriter.didChange()); } /** * Finds the method that overrides this method, starting with the target * class. */ private JMethod findOverridingMethod(JMethod method, JClassType target) { if (target == null) { return null; } for (JMethod overridingMethod : target.getMethods()) { if (JTypeOracle.methodsDoMatch(method, overridingMethod)) { return overridingMethod; } } return findOverridingMethod(method, target.getSuperClass()); } /** * Create a conditional method to discriminate between static and virtual * dispatch. * *
    * static boolean equals__devirtual$(Object this, Object other) {
    *   return Cast.isJavaObject(this) ? this.equals(other) : JavaScriptObject.equals$(this, other);
    * }
    * 
*/ private JMethod getOrCreateDevirtualMethod(JMethodCall polyMethodCall, JMethod jsoImpl) { JMethod polyMethod = polyMethodCall.getTarget(); /** * TODO(cromwellian) generate a inlined expression instead of Method Because * devirtualization happens after optimization, the devirtual methods don't * optimize well in the JS pass. Consider "inlining" a hand optimized * devirtual method at callsites instead of a JMethodCall. As a bonus, the * inlined code can be specialized for each callsite, for example, if there * are no side effects, then there's no need for a temporary. Or, if the * instance can't possibly be java.lang.String, then the JSO check becomes a * cheaper check for typeMarker. */ if (polyMethodToDevirtualMethods.containsKey(polyMethod)) { return polyMethodToDevirtualMethods.get(polyMethod); } JClassType jsoType = program.getJavaScriptObject(); SourceInfo sourceInfo = jsoType.getSourceInfo().makeChild(SourceOrigin.UNKNOWN); // Create the new method. String prefix; Integer methodCount; methodCount = jsoMethodInstances.get(polyMethod.getSignature()); if (methodCount == null) { prefix = polyMethod.getName(); methodCount = 0; } else { prefix = polyMethod.getName() + methodCount; methodCount++; } jsoMethodInstances.put(polyMethod.getSignature(), methodCount); String devirtualName = prefix + "__devirtual$"; JMethod newMethod = new JMethod(sourceInfo, devirtualName, jsoType, polyMethod.getType(), false, true, true, AccessModifier.PUBLIC); newMethod.setBody(new JMethodBody(sourceInfo)); jsoType.addMethod(newMethod); newMethod.setSynthetic(); // Setup parameters. JParameter thisParam = JProgram.createParameter(sourceInfo, "this$static", program.getTypeJavaLangObject(), true, true, newMethod); for (JParameter oldParam : polyMethod.getParams()) { JProgram.createParameter(sourceInfo, oldParam.getName(), oldParam.getType(), true, false, newMethod); } newMethod.freezeParamTypes(); newMethod.addThrownExceptions(polyMethod.getThrownExceptions()); sourceInfo.addCorrelation(sourceInfo.getCorrelator().by(newMethod)); // maybeJsoInvocation = this$static JLocal temp = JProgram.createLocal(sourceInfo, "maybeJsoInvocation", thisParam.getType(), true, (JMethodBody) newMethod.getBody()); JMultiExpression multi = new JMultiExpression(sourceInfo); // (maybeJsoInvocation = this$static, ) multi.exprs.add(JProgram.createAssignmentStmt(sourceInfo, new JLocalRef(sourceInfo, temp), new JParameterRef(sourceInfo, thisParam)).getExpr()); // Build from bottom up. // isJavaObject(temp) JMethodCall condition = new JMethodCall(sourceInfo, null, isJavaObjectMethod); condition.addArg(new JLocalRef(sourceInfo, temp)); // temp.method(args) JMethodCall thenValue = new JMethodCall(sourceInfo, new JLocalRef(sourceInfo, temp), polyMethod); for (JParameter param : newMethod.getParams()) { if (param != thisParam) { thenValue.addArg(new JParameterRef(sourceInfo, param)); } } // jso$method(temp, args) JMethodCall elseValue = new JMethodCall(sourceInfo, null, jsoImpl); elseValue.addArg(new JLocalRef(sourceInfo, temp)); for (JParameter param : newMethod.getParams()) { if (param != thisParam) { elseValue.addArg(new JParameterRef(sourceInfo, param)); } } // isJavaObject(temp) ? temp.method(args) : jso$method(temp, args) JConditional conditional = new JConditional(sourceInfo, polyMethod.getType(), condition, thenValue, elseValue); multi.exprs.add(conditional); JReturnStatement returnStatement = new JReturnStatement(sourceInfo, multi); ((JMethodBody) newMethod.getBody()).getBlock().addStmt(returnStatement); polyMethodToDevirtualMethods.put(polyMethod, newMethod); return newMethod; } private JMethod getStaticImpl(JMethod method) { assert !method.isStatic(); JMethod staticImpl = program.getStaticImpl(method); if (staticImpl == null) { staticImplCreator.accept(method); staticImpl = program.getStaticImpl(method); } return staticImpl; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy