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

com.oracle.truffle.api.interop.java.JavaInterop Maven / Gradle / Ivy

Go to download

Truffle is a multi-language framework for executing dynamic languages that achieves high performance when combined with Graal.

There is a newer version: 1.0.0-rc7
Show newest version
/*
 * Copyright (c) 2015, 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 com.oracle.truffle.api.interop.java;

import java.lang.reflect.Method;

import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.ForeignAccess;
import com.oracle.truffle.api.interop.InteropException;
import com.oracle.truffle.api.interop.Message;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.nodes.RootNode;
import java.lang.reflect.Modifier;

/**
 * Helper methods to simplify access to objects of {@link TruffleLanguage Truffle languages} from
 * Java and the other way around. The Java/Truffle interop builds on
 * {@link ForeignAccess mutual interoperability} between individual Truffle languages - it
 * just encapsulates it into Java facade to make it as natural to access foreign
 * {@link TruffleObject Truffle objects} as Java programmers are used to when accessing
 * Java objects and interfaces directly.
 *
 * 

Java/Truffle Object Inter-op Semantics

* * In case your language exposes a {@link TruffleObject} implementation, and somebody wraps your * object into a JavaInterop interface via * {@link #asJavaObject(java.lang.Class, com.oracle.truffle.api.interop.TruffleObject)} method, this * is the set of {@link Message messages} you can expect: *

* Users can send you any message by annotating their interface method with {@link MethodMessage} * and it is up to them (and you) to negotiate the correct set of messages and their parameters to * help you understand each other. However there is a default set of {@link Message messages} (for * methods not annotated by {@link MethodMessage}) which consists of: *

    *
  1. First of all {@link Message#createInvoke(int)} is constructed (with the number of parameters * of the interface method) and delivered to your object. The * {@link ForeignAccess#getReceiver(com.oracle.truffle.api.frame.Frame) receiver} of the message is * your {@link TruffleObject}. The first * {@link ForeignAccess#getArguments(com.oracle.truffle.api.frame.Frame) argument} is name of the * interface method, followed by the * {@link ForeignAccess#getArguments(com.oracle.truffle.api.frame.Frame) actual arguments} of the * interface method. Your language can either handle the message or throw * {@link UnsupportedMessageException} to signal additional processing is needed.
  2. *
  3. If the {@link Message#createInvoke(int) previous message} isn't handled, a * {@link Message#READ} is sent to your {@link TruffleObject object} (e.g. * {@link ForeignAccess#getReceiver(com.oracle.truffle.api.frame.Frame) receiver}) with a field name * equal to the name of the interface method. If the read returns a primitive type, it is returned. *
  4. *
  5. If the read value is another {@link TruffleObject}, it is inspected whether it handles * {@link Message#IS_EXECUTABLE}. If it does, a message {@link Message#createExecute(int)} with name * of the interface method and its parameters is sent to the object. The result is returned to the * interface method caller.
  6. *
  7. In case the read value is neither primitive, neither {@link Message#IS_EXECUTABLE executable} * , and the interface method has no parameters, it is returned back.
  8. *
  9. All other cases yield an {@link InteropException}.
  10. *
*

* Object oriented languages are expected to handle the initial {@link Message#createInvoke(int)} * message. Non-OOP languages are expected to ignore it, yield {@link UnsupportedMessageException} * and handle the subsequent {@link Message#READ read} and {@link Message#createExecute(int) * execute} ones. The real semantic however depends on the actual language one is communicating * with. *

* * @since 0.9 */ public final class JavaInterop { private JavaInterop() { } /** * Wraps a {@link TruffleObject foreign object} into easy to use interface. Imagine one wants to * access a JavaScript object like: * *

     * var obj = {
     *   'x' : 10,
     *   'y' : 3.3,
     *   'name' : 'Truffle'
     * };
     * 
* * from Java. One can do it by defining an interface: * *
     * interface ObjAccess {
     *   int x();
     *   {@link MethodMessage @MethodMessage}(message = "WRITE")
     *   void x(int newValue);
     *   double y();
     *   String name();
     * }
     * 
* * and obtaining its instance by calling this conversion method: * *
     * ObjAccess access = JavaInterop.{@link #asJavaObject(java.lang.Class, com.oracle.truffle.api.interop.TruffleObject) asJavaObject}(ObjAccess.class, obj);
     * assert access.x() == 10 : "Still the default";
     * access.x(5);
     * assert access.x() == 5 : "Changed to five";
     * 
* * @param type of requested and returned value * @param type interface modeling structure of foreignObject in Java * @param foreignObject object coming from a {@link TruffleObject Truffle language}, can be * null, in such case the returned value will likely be * null as well * @return instance of requested interface granting access to specified * foreignObject, can be null, if the foreignObject parameter * was null * @since 0.9 */ public static T asJavaObject(Class type, TruffleObject foreignObject) { RootNode root = new TemporaryConvertRoot(TruffleLanguage.class, new ToJavaNode(), foreignObject, type); Object convertedValue = Truffle.getRuntime().createCallTarget(root).call(); return type.cast(convertedValue); } /** * Exports a Java object for use in any {@link TruffleLanguage}. The system scans structure of * provided object and exposes all public fields and methods to any Truffle * language. An instance of class * *
     * class JavaRecord {
     *   public int x;
     *   public double y;
     *   public String name() {
     *     return "Truffle";
     *   }
     * }
     * {@link TruffleObject} obj = JavaInterop.asTruffleObject(new JavaRecord());
     * 
* * can then be access from JavaScript or any other Truffle based language as * *
     * obj.x;
     * obj.y;
     * obj.name();
     * 
* * When the obj represents a {@link Class}, then the created {@link TruffleObject} * will allow access to public and static fields and methods from the class. * * @param obj a Java object to convert into one suitable for Truffle languages * @return converted object * @since 0.9 */ public static TruffleObject asTruffleObject(Object obj) { if (obj instanceof TruffleObject) { return ((TruffleObject) obj); } if (obj instanceof Class) { return new JavaObject(null, (Class) obj); } if (obj == null) { return JavaObject.NULL; } return new JavaObject(obj, obj.getClass()); } /** * Takes executable object from a {@link TruffleLanguage} and converts it into an instance of a * Java functional interface. * * @param requested and returned type * @param functionalType interface with a single defined method - so called * functional interface * @param function Truffle that responds to {@link Message#IS_EXECUTABLE} and can be * invoked * @return instance of interface that wraps the provided function * @since 0.9 */ public static T asJavaFunction(Class functionalType, TruffleObject function) { RootNode root = new TemporaryConvertRoot(TruffleLanguage.class, new ToJavaNode(), function, functionalType); return functionalType.cast(Truffle.getRuntime().createCallTarget(root).call()); } /** * Takes a functional interface and its implementation (for example lambda function) and * converts it into object executable by Truffle languages. Here is a definition of * function returning the meaning of life as lambda expression, converting it back to * Java and using it: * *
     * TruffleObject to = JavaInterop.asTruffleFunction(Callable.class, () -> 42);
     * Callable c = JavaInterop.{@link #asJavaFunction(java.lang.Class, com.oracle.truffle.api.interop.TruffleObject) asJavaFunction}(Callable.class, to);
     * assert c.call() == 42;
     * 
* * @param requested interface and implementation * @param functionalType interface with a single defined method - so called * functional interface * @param implementation implementation of the interface, or directly a lambda expression * defining the required behavior * @return an {@link Message#IS_EXECUTABLE executable} {@link TruffleObject} ready to be used in * any Truffle language * @since 0.9 */ public static TruffleObject asTruffleFunction(Class functionalType, T implementation) { final Method method = functionalInterfaceMethod(functionalType); if (method == null) { throw new IllegalArgumentException(); } return new JavaFunctionObject(method, implementation); } private static Method functionalInterfaceMethod(Class functionalType) { if (!functionalType.isInterface()) { return null; } final Method[] arr = functionalType.getMethods(); if (arr.length == 1) { return arr[0]; } Method found = null; for (Method m : arr) { if ((m.getModifiers() & Modifier.ABSTRACT) == 0) { continue; } try { Object.class.getMethod(m.getName(), m.getParameterTypes()); continue; } catch (NoSuchMethodException ex) { // OK, not an object method } if (found != null) { return null; } found = m; } return found; } private static class TemporaryConvertRoot extends RootNode { @Child private ToJavaNode node; private final Object value; private final Class type; @SuppressWarnings("rawtypes") TemporaryConvertRoot(Class lang, ToJavaNode node, Object value, Class type) { super(lang, null, null); this.node = node; this.value = value; this.type = type; } @Override public Object execute(VirtualFrame frame) { return node.convert(frame, value, type); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy