jaspex.speculation.SpeculativeClassLoader.java Source code

Java tutorial

Introduction

Here is the source code for jaspex.speculation.SpeculativeClassLoader.java

Source

/*
 * jaspex-mls: a Java Software Speculative Parallelization Framework
 * Copyright (C) 2015 Ivo Anjo <ivo.anjo@ist.utl.pt>
 *
 * This file is part of jaspex-mls.
 *
 * jaspex-mls is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * jaspex-mls is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with jaspex-mls.  If not, see <http://www.gnu.org/licenses/>.
 */

package jaspex.speculation;

import jaspex.ClassFilter;
import jaspex.Options;
import jaspex.speculation.nsruntime.ContSpeculationControl;
import jaspex.speculation.runtime.*;
import jaspex.stm.ExternalAccessHelper;
import jaspex.transactifier.Transactifier;
import jaspex.util.Unsafe;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.*;
import java.util.jar.Attributes;
import java.util.jar.JarFile;

import org.objectweb.asm.Opcodes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import asmlib.Type;

import util.StringList;

import static jaspex.util.ShellColor.color;

/** ClassLoader que passa classes pelo Transactifier e pelo SpeculativeTransformer antes de as carregar na VM **/
public class SpeculativeClassLoader extends ClassLoader {

    private static final Logger Log = LoggerFactory.getLogger(SpeculativeClassLoader.class);

    public static final SpeculativeClassLoader INSTANCE = new SpeculativeClassLoader();

    private SpeculativeClassLoader() {
        if (jaspex.Options.CLASSCACHE) {
            Cache.tryRestore();
        }
    }

    @Override
    protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        String mode = color("[ NormalLoad ]", "32");
        Type classType = Type.fromCommon(name);

        try {
            Class<?> c = findLoadedClass(name);
            if (c != null)
                return c;

            // No se podem transactificar coisas java.* + coisas nossas
            if (!ClassFilter.isTransactifiable(classType) && !CodegenHelper.isCodegenClass(classType)
                    && !ExternalAccessHelper.isExternalAccessClass(classType)) {
                mode = color("[ UpstreamLoader ]", "35");

                if (name.startsWith("java.util.concurrent.Future$")) {
                    // Bug newspec
                    throw new AssertionError("Tried to load Future with extra type info");
                }

                return getParent().loadClass(name);
            }

            c = findClass(name);
            if (resolve)
                resolveClass(c);
            return c;
        } finally {
            if ((!name.startsWith("java.") || Options.TRANSACTIFYJDK) && !name.startsWith("jaspex.")) {
                Log.trace("{} Loading {}", mode, name);
            }
            if (CodegenHelper.isCodegenClass(classType)) {
                Log.trace("Loaded codegen class {}", name.replace("jaspex.speculation.runtime.codegen.", ""));
            }
        }
    }

    @Override
    protected Class<?> findClass(String className) throws ClassNotFoundException {
        byte[] classFile;
        try {
            long startTime = System.currentTimeMillis();
            classFile = getClassBytes(Type.fromCommon(className));
            long prepareTime = System.currentTimeMillis() - startTime;
            if (prepareTime > 1000) {
                Log.warn("Needed " + ((double) prepareTime) / 1000 + "s to prepare " + className);
            }
        } catch (IOException e) {
            throw new ClassNotFoundException("Could not load class " + className, e);
        } catch (RuntimeException e) {
            Log.error("Error while preparing class " + className, e);
            throw e;
        } catch (Error e) {
            Log.error("Error while preparing class " + className, e);
            throw e;
        }

        // Verificar que classe j no foi carregada entretanto (CheckClassAdapter pode causar isso)
        Class<?> c = findLoadedClass(className);
        if (c != null) {
            Log.warn("Class {} was already loaded during findClass", className);
            return c;
        }

        try {
            if (Options.TRANSACTIFYJDK) {
                // Usar UNSAFE para carregar classe, fazendo bypass s verificaes que impedem
                // o carregamento de classes da JDK
                return Unsafe.UNSAFE.defineClass(className, classFile, 0, classFile.length, this,
                        getDefaultProtectionDomain());
            } else {
                return defineClass(className, classFile, 0, classFile.length);
            }
        } catch (SecurityException e) {
            throw new Error(e);
        }
    }

    // Baseado em http://stackoverflow.com/questions/271506/
    // Adiciona jarFile ao classpath actual, como se tivesse sido passado por -cp ou $CLASSPATH
    private static void addToClasspath(File jarFile) throws Exception {
        java.net.URL url = jarFile.toURI().toURL();
        java.lang.reflect.Method m = java.net.URLClassLoader.class.getDeclaredMethod("addURL", java.net.URL.class);
        m.setAccessible(true);
        m.invoke(ClassLoader.getSystemClassLoader(), url);
        //  raro, mas j encontrei um programa que gosta de ir consultar isto, e depois no
        // se encontra
        System.setProperty("java.class.path",
                jarFile.getAbsolutePath() + File.pathSeparatorChar + System.getProperty("java.class.path"));
    }

    protected byte[] getClassBytes(Type className) throws IOException {
        byte[] classBytes;

        if (jaspex.Options.CLASSCACHE) {
            classBytes = Cache.lookupClass(className);
            if (classBytes != null)
                return classBytes;
        }

        if (CodegenHelper.isCodegenClass(className)) {
            return CodegenHelper.generateClass(className);
        } else if (ExternalAccessHelper.isExternalAccessClass(className)) {
            return ExternalAccessHelper.generateClass(className);
        } else {
            classBytes = new Transactifier(className).transform();
            classBytes = new SpeculativeTransformer(false).transform(classBytes);
            if (jaspex.Options.CLASSCACHE) {
                Cache.saveClass(className, classBytes);
            }
            return classBytes;
        }
    }

    private java.security.ProtectionDomain getDefaultProtectionDomain() {
        try {
            Field f = ClassLoader.class.getDeclaredField("defaultDomain");
            f.setAccessible(true);
            return (java.security.ProtectionDomain) f.get(this);
        } catch (Exception e) {
            throw new Error(e);
        }
    }

    public void execute(StringList args) throws Throwable {
        Log.info("JaSPEx SpeculativeClassLoader (Start time: {}, Args: '{}')",
                new java.text.SimpleDateFormat("HH:mm:ss").format(new java.util.Date()), args.join(" "));

        if (Options.JAR) {
            File jar = new File(args.pollFirst());
            addToClasspath(jar);
            JarFile jarFile = new JarFile(jar);
            String mainClass = jarFile.getManifest().getMainAttributes().getValue(Attributes.Name.MAIN_CLASS);
            jarFile.close();
            args.addFirst(mainClass);
        }

        // Infelizmente no podemos usar reflection para invocar mtodos dentro do
        // runWithContinuationSupport, portanto abusamos do codegen para obter um
        // objecto callable que chame o main
        Type codegenType = CodegenHelper
                .methodToCodegenType(new InvokedMethod(Opcodes.INVOKESTATIC, Type.fromCommon(args.pollFirst()),
                        "main$speculative", "(" + Type.STRING.toArray().bytecodeName() + ")V"));

        final Callable c = (Callable) loadClass(codegenType.commonName(), true).getConstructor(String[].class)
                .newInstance((Object) args.toArray());

        // Passamos o objecto que quando invocado chama o main para o ContSpeculationControl
        ContSpeculationControl.bootstrapMain(c);
    }

}