com.google.template.soy.jbcsrc.ClassData.java Source code

Java tutorial

Introduction

Here is the source code for com.google.template.soy.jbcsrc.ClassData.java

Source

/*
 * Copyright 2015 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.template.soy.jbcsrc;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.util.CheckClassAdapter;
import org.objectweb.asm.util.Textifier;
import org.objectweb.asm.util.TraceClassVisitor;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;

/**
 * A simple tuple of generated class data and type information about the class.
 * 
 * <p>Note: not using an @AutoValue since it copies
 */
final class ClassData {
    static ClassData create(TypeInfo type, byte[] b, int numFields, int numDetachStates) {
        return new ClassData(type, b, numFields, numDetachStates);
    }

    private final TypeInfo type;
    private final byte[] data;
    private final int numberOfFields;
    private final int numDetachStates;

    private ClassData(TypeInfo type, byte[] data, int numberOfFields, int numDetachStates) {
        this.type = checkNotNull(type);
        this.data = checkNotNull(data);
        this.numberOfFields = numberOfFields;
        this.numDetachStates = numDetachStates;
    }

    TypeInfo type() {
        return type;
    }

    /**
     * Caution, this returns the underlying array and is mutable.
     */
    byte[] data() {
        return data;
    }

    int numberOfFields() {
        return numberOfFields;
    }

    int numberOfDetachStates() {
        return numDetachStates;
    }

    /**
     * Runs the {@link CheckClassAdapter} on this class in basic analysis mode.
     *
     * <p>Basic anaylsis mode can flag verification errors that don't depend on knowing complete type
     * information for the classes and methods being called.  This is useful for flagging simple
     * generation mistakes (e.g. stack underflows, method return type mismatches, accessing invalid
     * locals).  Additionally, the error messages are more useful than what the java verifier normally
     * presents.
     */
    void checkClass() {
        ClassNode cv = new ClassNode();
        new ClassReader(data).accept(new CheckClassAdapter(cv, true /* check data flow */), 0);
        // double check our fields while we are here.
        checkState(type.internalName().equals(cv.name));
        checkState(numberOfFields == cv.fields.size());
    }

    URL asUrl() {
        try {
            // Needs specifyStreamHandler permission.
            return AccessController.doPrivileged(new PrivilegedExceptionAction<URL>() {
                @Override
                public URL run() throws MalformedURLException {
                    return new URL("mem", "", -1, type.internalName() + ".class", new URLStreamHandler() {
                        @Override
                        protected URLConnection openConnection(URL u) {
                            return new URLConnection(u) {
                                @Override
                                public void connect() {
                                }

                                @Override
                                public InputStream getInputStream() {
                                    return new ByteArrayInputStream(data);
                                }
                            };
                        }
                    });
                }
            });
        } catch (Exception e) {
            throw new IllegalStateException("Failed to create stream handler for resource url", e);
        }
    }

    @Override
    public String toString() {
        StringWriter sw = new StringWriter();
        new ClassReader(data).accept(new TraceClassVisitor(null, new Textifier(), new PrintWriter(sw)), 0);
        return sw.toString();
    }
}