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

jdk.jshell.spi.ExecutionControl Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package jdk.jshell.spi;

import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;

/**
 * This interface specifies the functionality that must provided to implement a
 * pluggable JShell execution engine.
 * 

* The audience for this Service Provider Interface is engineers wishing to * implement their own version of the execution engine in support of the JShell * API. *

* A Snippet is compiled into code wrapped in a 'wrapper class'. The execution * engine is used by the core JShell implementation to load and, for executable * Snippets, execute the Snippet. *

* Methods defined in this interface should only be called by the core JShell * implementation. * * @since 9 */ public interface ExecutionControl extends AutoCloseable { /** * Attempts to load new classes. * * @param cbcs the class name and bytecodes to load * @throws ClassInstallException exception occurred loading the classes, * some or all were not loaded * @throws NotImplementedException if not implemented * @throws EngineTerminationException the execution engine has terminated */ void load(ClassBytecodes[] cbcs) throws ClassInstallException, NotImplementedException, EngineTerminationException; /** * Attempts to redefine previously loaded classes. * * @param cbcs the class name and bytecodes to redefine * @throws ClassInstallException exception occurred redefining the classes, * some or all were not redefined * @throws NotImplementedException if not implemented * @throws EngineTerminationException the execution engine has terminated */ void redefine(ClassBytecodes[] cbcs) throws ClassInstallException, NotImplementedException, EngineTerminationException; /** * Invokes an executable Snippet by calling a method on the specified * wrapper class. The method must have no arguments and return String. * * @param className the class whose method should be invoked * @param methodName the name of method to invoke * @return the result of the execution or null if no result * @throws UserException the invoke raised a user exception * @throws ResolutionException the invoke attempted to directly or * indirectly invoke an unresolved snippet * @throws StoppedException if the {@code invoke()} was canceled by * {@link ExecutionControl#stop} * @throws EngineTerminationException the execution engine has terminated * @throws InternalException an internal problem occurred */ String invoke(String className, String methodName) throws RunException, EngineTerminationException, InternalException; /** * Returns the value of a variable. * * @param className the name of the wrapper class of the variable * @param varName the name of the variable * @return the value of the variable * @throws UserException formatting the value raised a user exception * @throws ResolutionException formatting the value attempted to directly or * indirectly invoke an unresolved snippet * @throws StoppedException if the formatting the value was canceled by * {@link ExecutionControl#stop} * @throws EngineTerminationException the execution engine has terminated * @throws InternalException an internal problem occurred */ String varValue(String className, String varName) throws RunException, EngineTerminationException, InternalException; /** * Adds the path to the execution class path. * * @param path the path to add * @throws EngineTerminationException the execution engine has terminated * @throws InternalException an internal problem occurred */ void addToClasspath(String path) throws EngineTerminationException, InternalException; /** * Interrupts a running invoke. * * @throws EngineTerminationException the execution engine has terminated * @throws InternalException an internal problem occurred */ void stop() throws EngineTerminationException, InternalException; /** * Run a non-standard command (or a standard command from a newer version). * * @param command the non-standard command * @param arg the commands argument * @return the commands return value * @throws UserException the command raised a user exception * @throws ResolutionException the command attempted to directly or * indirectly invoke an unresolved snippet * @throws StoppedException if the command was canceled by * {@link ExecutionControl#stop} * @throws EngineTerminationException the execution engine has terminated * @throws NotImplementedException if not implemented * @throws InternalException an internal problem occurred */ Object extensionCommand(String command, Object arg) throws RunException, EngineTerminationException, InternalException; /** * Shuts down this execution engine. Implementation should free all * resources held by this execution engine. *

* No calls to methods on this interface should be made after close. */ @Override void close(); /** * Search for a provider, then create and return the * {@code ExecutionControl} instance. * * @param env the execution environment (provided by JShell) * @param name the name of provider * @param parameters the parameter map. * @return the execution engine * @throws Throwable an exception that occurred attempting to find or create * the execution engine. * @throws IllegalArgumentException if no ExecutionControlProvider has the * specified {@code name} and {@code parameters}. */ static ExecutionControl generate(ExecutionEnv env, String name, Map parameters) throws Throwable { Set keys = parameters == null ? Collections.emptySet() : parameters.keySet(); for (ExecutionControlProvider p : ServiceLoader.load(ExecutionControlProvider.class)) { if (p.name().equals(name) && p.defaultParameters().keySet().containsAll(keys)) { return p.generate(env, parameters); } } throw new IllegalArgumentException("No ExecutionControlProvider with name '" + name + "' and parameter keys: " + keys.toString()); } /** * Search for a provider, then create and return the * {@code ExecutionControl} instance. * * @param env the execution environment (provided by JShell) * @param spec the {@code ExecutionControl} spec, which is described in * the documentation of this * {@linkplain jdk.jshell.spi package documentation}. * @return the execution engine * @throws Throwable an exception that occurred attempting to find or create * the execution engine. * @throws IllegalArgumentException if no ExecutionControlProvider has the * specified {@code name} and {@code parameters}. * @throws IllegalArgumentException if {@code spec} is malformed */ static ExecutionControl generate(ExecutionEnv env, String spec) throws Throwable { class SpecReader { int len = spec.length(); int i = -1; char ch; SpecReader() { next(); } boolean more() { return i < len; } char current() { return ch; } final boolean next() { ++i; if (i < len) { ch = spec.charAt(i); return true; } i = len; return false; } void skipWhite() { while (more() && Character.isWhitespace(ch)) { next(); } } String readId() { skipWhite(); StringBuilder sb = new StringBuilder(); while (more() && Character.isJavaIdentifierPart(ch)) { sb.append(ch); next(); } skipWhite(); String id = sb.toString(); if (id.isEmpty()) { throw new IllegalArgumentException("Expected identifier in " + spec); } return id; } void expect(char exp) { skipWhite(); if (!more() || ch != exp) { throw new IllegalArgumentException("Expected '" + exp + "' in " + spec); } next(); skipWhite(); } String readValue() { expect('('); int parenDepth = 1; StringBuilder sb = new StringBuilder(); while (more()) { if (ch == ')') { --parenDepth; if (parenDepth == 0) { break; } } else if (ch == '(') { ++parenDepth; } sb.append(ch); next(); } expect(')'); return sb.toString(); } } Map parameters = new HashMap<>(); SpecReader sr = new SpecReader(); String name = sr.readId(); if (sr.more()) { sr.expect(':'); while (sr.more()) { String key = sr.readId(); String value = sr.readValue(); parameters.put(key, value); if (sr.more()) { sr.expect(','); } } } return generate(env, name, parameters); } /** * Bundles class name with class bytecodes. */ public static final class ClassBytecodes implements Serializable { private static final long serialVersionUID = 0xC1A55B47EC0DE5L; private final String name; private final byte[] bytecodes; /** * Creates a name/bytecode pair. * @param name the class name * @param bytecodes the class bytecodes */ public ClassBytecodes(String name, byte[] bytecodes) { this.name = name; this.bytecodes = bytecodes; } /** * The bytecodes for the class. * * @return the bytecodes */ public byte[] bytecodes() { return bytecodes; } /** * The class name. * * @return the class name */ public String name() { return name; } } /** * The abstract base of all {@code ExecutionControl} exceptions. */ public static abstract class ExecutionControlException extends Exception { private static final long serialVersionUID = 1L; public ExecutionControlException(String message) { super(message); } } /** * Unbidden execution engine termination has occurred. */ public static class EngineTerminationException extends ExecutionControlException { private static final long serialVersionUID = 1L; public EngineTerminationException(String message) { super(message); } } /** * The command is not implemented. */ public static class NotImplementedException extends InternalException { private static final long serialVersionUID = 1L; public NotImplementedException(String message) { super(message); } } /** * An internal problem has occurred. */ public static class InternalException extends ExecutionControlException { private static final long serialVersionUID = 1L; public InternalException(String message) { super(message); } } /** * A class install (load or redefine) encountered a problem. */ public static class ClassInstallException extends ExecutionControlException { private static final long serialVersionUID = 1L; private final boolean[] installed; public ClassInstallException(String message, boolean[] installed) { super(message); this.installed = installed; } /** * Indicates which of the passed classes were successfully * loaded/redefined. * @return a one-to-one array with the {@link ClassBytecodes}{@code[]} * array -- {@code true} if installed */ public boolean[] installed() { return installed; } } /** * The abstract base of of exceptions specific to running user code. */ public static abstract class RunException extends ExecutionControlException { private static final long serialVersionUID = 1L; private RunException(String message) { super(message); } } /** * A 'normal' user exception occurred. */ public static class UserException extends RunException { private static final long serialVersionUID = 1L; private final String causeExceptionClass; public UserException(String message, String causeExceptionClass, StackTraceElement[] stackElements) { super(message); this.causeExceptionClass = causeExceptionClass; this.setStackTrace(stackElements); } /** * Returns the class of the user exception. * @return the name of the user exception class */ public String causeExceptionClass() { return causeExceptionClass; } } /** * An exception indicating that a {@code DeclarationSnippet} with unresolved * references has been encountered. *

* Contrast this with the initiating {@link SPIResolutionException} * (a {@code RuntimeException}) which is embedded in generated corralled * code. Also, contrast this with * {@link jdk.jshell.UnresolvedReferenceException} the high-level * exception (with {@code DeclarationSnippet} reference) provided in the * main API. */ public static class ResolutionException extends RunException { private static final long serialVersionUID = 1L; private final int id; /** * Constructs an exception indicating that a {@code DeclarationSnippet} * with unresolved references has been encountered. * * @param id An internal identifier of the specific method * @param stackElements the stack trace */ public ResolutionException(int id, StackTraceElement[] stackElements) { super("resolution exception: " + id); this.id = id; this.setStackTrace(stackElements); } /** * Retrieves the internal identifier of the unresolved identifier. * * @return the internal identifier */ public int id() { return id; } } /** * An exception indicating that an * {@link ExecutionControl#invoke(java.lang.String, java.lang.String) } * (or theoretically a * {@link ExecutionControl#varValue(java.lang.String, java.lang.String) }) * has been interrupted by a {@link ExecutionControl#stop() }. */ public static class StoppedException extends RunException { private static final long serialVersionUID = 1L; public StoppedException() { super("stopped by stop()"); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy