Package com.googlecode.gwt.test.internal.rewrite

Source Code of com.googlecode.gwt.test.internal.rewrite.WriteJsoImpl$ForJsoInterface

/*
* 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.googlecode.gwt.test.internal.rewrite;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import com.google.gwt.dev.asm.ClassVisitor;
import com.google.gwt.dev.asm.FieldVisitor;
import com.google.gwt.dev.asm.MethodVisitor;
import com.google.gwt.dev.asm.Opcodes;
import com.google.gwt.dev.asm.Type;
import com.google.gwt.dev.asm.commons.Method;
import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.InstanceMethodOracle;
import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.SingleJsoImplData;
import com.googlecode.gwt.test.internal.utils.JsoProperties;

/**
* Writes the implementation classes for JSO and its subtypes.
*
* Changes made by the base class:
* <ol>
* <li>The new type has the same name as the old type with a '$' appended.</li>
* <li>All instance methods in the original type become static methods taking an explicit
* <code>this</code> parameter. Such methods have the same stack behavior as the original.</li>
* </ol>
*/
abstract class WriteJsoImpl extends ClassVisitor {

   /**
    * This type implements JavaScriptObject.
    *
    * <ol>
    * <li>JavaScriptObject itself gets a new synthetic field to store the underlying hosted mode
    * reference.</li>
    * <li>Instance methods are added so that JavaScriptObject implements all SingleJsoImpl
    * interfaces.</li>
    * </ol>
    *
    */
   private static class ForJsoDollar extends WriteJsoImpl {
      private final SingleJsoImplData jsoData;
      /**
       * An unmodifiable set of descriptors containing <code>JavaScriptObject</code> and all
       * subclasses.
       */
      private final Set<String> jsoDescriptors;

      public ForJsoDollar(ClassVisitor cv, Set<String> jsoDescriptors, InstanceMethodOracle mapper,
               SingleJsoImplData jsoData) {
         super(cv, mapper);
         this.jsoDescriptors = jsoDescriptors;
         this.jsoData = jsoData;
      }

      @Override
      public void visit(int version, int access, String name, String signature, String superName,
               String[] interfaces) {

         ArrayList<String> jsoDescList = new ArrayList<String>();
         jsoDescList.addAll(jsoDescriptors);
         interfaces = jsoDescList.toArray(new String[jsoDescList.size()]);

         super.visit(version, access, name, signature, superName, interfaces);

         FieldVisitor fv = visitField(Opcodes.ACC_PROTECTED | Opcodes.ACC_SYNTHETIC,
                  JsoProperties.JSO_PROPERTIES,
                  "Lcom/googlecode/gwt/test/internal/utils/PropertyContainer;", null, null);
         if (fv != null) {
            fv.visitEnd();
         }

         // Implement the trampoline methods
         for (String mangledName : jsoData.getMangledNames()) {
            List<Method> declarations = jsoData.getDeclarations(mangledName);
            List<Method> implementations = jsoData.getImplementations(mangledName);
            assert declarations.size() == implementations.size() : "Declaration / implementation size mismatch";

            Iterator<Method> declIterator = declarations.iterator();
            Iterator<Method> implIterator = implementations.iterator();

            while (declIterator.hasNext()) {
               assert implIterator.hasNext();
               writeTrampoline(mangledName, declIterator.next(), implIterator.next());
            }
         }
      }

      @Override
      public MethodVisitor visitMethod(int access, String name, String desc, String signature,
               String[] exceptions) {
         if (isCtor(name)) {
            // make the JavaScriptObject$ constructor public
            access &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED);
            access |= Opcodes.ACC_PUBLIC;
         }
         return super.visitMethod(access, name, desc, signature, exceptions);
      }

      /**
       * JSO methods are implemented as flyweight style, with the instance being passed as the first
       * parameter. This loop create instance methods on JSO$ for all of the mangled SingleJsoImpl
       * interface method names. These instance methods simply turn around and call the
       * static-dispatch methods. In Java, it might look like:
       *
       * <pre>
     * interface Interface {
     *   String someMethod(int a, double b);
     * }
     *
     * class J extends JSO implements I {
     *   public String com_google_Interface_someMethod(int a, double b) {
     *     return com.google.MyJso$.someMethod$(this, a, b);
     *   }
     * }
     * </pre>
       *
       * @param mangledName {@code com_google_gwt_sample_hello_client_Interface_a}
       * @param interfaceMethod {@code java.lang.String a(int, double)}
       * @param implementingMethod {@code static final java.lang.String
       *          a$(com.google.gwt.sample.hello.client.Jso, ...);}
       */
      private void writeTrampoline(String mangledName, Method interfaceMethod,
               Method implementingMethod) {
         assert implementingMethod.getArgumentTypes().length > 0;

         /*
          * The local descriptor is the same as the descriptor from the abstract method in the
          * interface.
          */
         String localDescriptor = interfaceMethod.getDescriptor();
         Method localMethod = new Method(mangledName, localDescriptor);

         /*
          * We also use the first argument to know which type to statically dispatch to.
          */
         Type implementingType = Type.getType("L"
                  + implementingMethod.getArgumentTypes()[0].getInternalName() + "$;");

         // Maybe create the method. This is marked final as a sanity check
         MethodVisitor mv = visitMethodNoRewrite(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL
                  | Opcodes.ACC_SYNTHETIC, localMethod.getName(), localMethod.getDescriptor(),
                  null, null);

         if (mv != null) {
            mv.visitCode();

            /*
             * It just so happens that the stack and local variable sizes are the same, but they're
             * kept distinct to aid in clarity should the dispatch logic change.
             */
            int var = 0;
            int size = 0;

            for (Type t : implementingMethod.getArgumentTypes()) {
               size += t.getSize();
               mv.visitVarInsn(t.getOpcode(Opcodes.ILOAD), var);
               var += t.getSize();
            }

            // Make sure there's enough room for the return value
            size = Math.max(size, implementingMethod.getReturnType().getSize());

            mv.visitMethodInsn(Opcodes.INVOKESTATIC, implementingType.getInternalName(),
                     implementingMethod.getName(), implementingMethod.getDescriptor());
            mv.visitInsn(localMethod.getReturnType().getOpcode(Opcodes.IRETURN));
            mv.visitMaxs(size, var);
            mv.visitEnd();
         }
      }
   }

   /**
    * This type is used to implement subtypes of JSO.
    *
    * <ol>
    * <li>The new type's superclass is mangled by adding $.</li>
    * <li>Constructors are deleted.</li>
    * </ol>
    */
   private static class ForJsoInterface extends WriteJsoImpl {
      public ForJsoInterface(ClassVisitor cv, InstanceMethodOracle mapper) {
         super(cv, mapper);
      }

      @Override
      public void visit(int version, int access, String name, String signature, String superName,
               String[] interfaces) {
         // Reference the old superclass's implementation class.
         superName += '$';
         interfaces = null;

         super.visit(version, access, name, signature, superName, interfaces);
      }

      @Override
      public MethodVisitor visitMethod(int access, String name, String desc, String signature,
               String[] exceptions) {
         boolean isCtor = isCtor(name);
         if (isCtor) {
            // Don't copy over constructors except for JavaScriptObject itself.
            return null;
         }
         return super.visitMethod(access, name, desc, signature, exceptions);
      }
   }

   /**
    * Creates a ClassVisitor to implement a JavaScriptObject subtype. This will select between a
    * simple implementation for user-defined JSO subtypes and the complex implementation for
    * implementing JavaScriptObject$.
    */
   public static ClassVisitor create(ClassVisitor cv, String classDescriptor,
            Set<String> jsoDescriptors, InstanceMethodOracle mapper,
            SingleJsoImplData singleJsoImplData) {

      if (classDescriptor.equals(OverlayTypesRewriter.JAVASCRIPTOBJECT_IMPL_DESC)) {
         return new ForJsoDollar(cv, jsoDescriptors, mapper, singleJsoImplData);
      }

      return new ForJsoInterface(cv, mapper);
   }

   /**
    * Maps methods to the class in which they are declared.
    */
   private final InstanceMethodOracle mapper;

   /**
    * The original name of the class being visited.
    */
   private String originalName;

   /**
    * Construct a new rewriter instance.
    *
    * @param cv the visitor to chain to
    * @param jsoDescriptors an unmodifiable set of descriptors containing
    *           <code>JavaScriptObject</code> and all subclasses
    * @param mapper maps methods to the class in which they are declared
    */
   private WriteJsoImpl(ClassVisitor cv, InstanceMethodOracle mapper) {
      super(Opcodes.ASM4, cv);
      this.mapper = mapper;
   }

   /**
    * Records the original name and resets access opcodes.
    */
   @Override
   public void visit(int version, int access, String name, String signature, String superName,
            String[] interfaces) {
      originalName = name;
      super.visit(version, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC,
               name + '$', signature, superName, interfaces);
   }

   /**
    * Mangle all instance methods declared in JavaScriptObject types.
    */
   @Override
   public MethodVisitor visitMethod(int access, String name, String desc, String signature,
            String[] exceptions) {
      boolean isCtor = isCtor(name);
      if (!isCtor && !isStatic(access) && !isObjectMethod(name + desc)) {
         access |= Opcodes.ACC_STATIC;
         desc = OverlayTypesRewriter.addSyntheticThisParam(getOriginalName(), desc);
         name = name + "$";
      }
      return super.visitMethod(access, name, desc, signature, exceptions);
   }

   protected String getOriginalName() {
      return originalName;
   }

   protected boolean isCtor(String name) {
      return "<init>".equals(name);
   }

   protected boolean isObjectMethod(String signature) {
      return "java/lang/Object".equals(mapper.findOriginalDeclaringClass(originalName, signature));
   }

   protected boolean isStatic(int access) {
      return (access & Opcodes.ACC_STATIC) != 0;
   }

   /**
    * Allows access to an unmodified visitMethod call.
    */
   protected MethodVisitor visitMethodNoRewrite(int access, String name, String desc,
            String signature, String[] exceptions) {
      return super.visitMethod(access, name, desc, signature, exceptions);
   }
}
TOP

Related Classes of com.googlecode.gwt.test.internal.rewrite.WriteJsoImpl$ForJsoInterface

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.