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

com.google.gwt.dev.shell.rewrite.WriteJsoImpl Maven / Gradle / Ivy

/*
 * 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.shell.rewrite;

import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.InstanceMethodOracle;
import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.SingleJsoImplData;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.Method;

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

/**
 * Writes the implementation classes for JSO and its subtypes.
 *
 * Changes made by the base class:
 * 
    *
  1. The new type has the same name as the old type with a '$' appended.
  2. *
  3. All instance methods in the original type become static methods taking an * explicit this parameter. Such methods have the same stack * behavior as the original.
  4. *
*/ abstract class WriteJsoImpl extends ClassVisitor { /** * This type implements JavaScriptObject. * *
    *
  1. JavaScriptObject itself gets a new synthetic field to store the * underlying hosted mode reference.
  2. *
  3. Instance methods are added so that JavaScriptObject implements all * SingleJsoImpl interfaces.
  4. *
* */ private static class ForJsoDollar extends WriteJsoImpl { /** * An unmodifiable set of descriptors containing * JavaScriptObject and all subclasses. */ private final Set jsoDescriptors; private final SingleJsoImplData jsoData; public ForJsoDollar(ClassVisitor cv, Set 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 jsoDescList = new ArrayList(); jsoDescList.addAll(jsoDescriptors); interfaces = jsoDescList.toArray(new String[jsoDescList.size()]); super.visit(version, access, name, signature, superName, interfaces); /* * Generate the synthetic "hostedModeReferece" field to contain the * underlying real reference to the JavaScript object. */ FieldVisitor fv = visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, HostedModeClassRewriter.REFERENCE_FIELD, "Ljava/lang/Object;", null, null); if (fv != null) { fv.visitEnd(); } // Implement the trampoline methods for (String mangledName : jsoData.getMangledNames()) { List declarations = jsoData.getDeclarations(mangledName); List implementations = jsoData.getImplementations(mangledName); assert declarations.size() == implementations.size() : "Declaration / implementation size mismatch"; Iterator declIterator = declarations.iterator(); Iterator 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: * *
     * 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);
     *   }
     * }
     * 
* * @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(), false); mv.visitInsn(localMethod.getReturnType().getOpcode(Opcodes.IRETURN)); mv.visitMaxs(size, var); mv.visitEnd(); } } } /** * This type is used to implement subtypes of JSO. * *
    *
  1. The new type's superclass is mangled by adding $.
  2. *
  3. Constructors are deleted.
  4. *
*/ 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 jsoDescriptors, InstanceMethodOracle mapper, SingleJsoImplData singleJsoImplData) { if (classDescriptor.equals(HostedModeClassRewriter.JAVASCRIPTOBJECT_IMPL_DESC)) { return new ForJsoDollar(cv, jsoDescriptors, mapper, singleJsoImplData); } else { 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 * JavaScriptObject and all subclasses * @param mapper maps methods to the class in which they are declared */ private WriteJsoImpl(ClassVisitor cv, InstanceMethodOracle mapper) { super(Opcodes.ASM5, 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 = HostedModeClassRewriter.addSyntheticThisParam(getOriginalName(), desc); name = name + "$"; } return super.visitMethod(access, name, desc, signature, exceptions); } protected String getOriginalName() { return originalName; } protected boolean isCtor(String name) { return "".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); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy