changeset 22200:7abcbeb12d08

Creating a TruffleObject that knows its PolyglotEngine and wrapping all values returned from the engine by it, so it is always clear into which engine context an object belongs.
author Jaroslav Tulach <jaroslav.tulach@oracle.com>
date Fri, 25 Sep 2015 16:53:27 +0200
parents e3aef4c65ea1
children df6a1647cfb3
files truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/EngineTruffleObject.java truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/JavaWrapper.java truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/PolyglotEngine.java truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLJavaInteropTest.java truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/TruffleTCK.java
diffstat 5 files changed, 207 insertions(+), 108 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/EngineTruffleObject.java	Fri Sep 25 16:53:27 2015 +0200
@@ -0,0 +1,82 @@
+/*
+ * 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.vm;
+
+import com.oracle.truffle.api.CallTarget;
+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.Message;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.RootNode;
+import java.io.IOException;
+
+final class EngineTruffleObject implements TruffleObject, ForeignAccess.Factory {
+    private final PolyglotEngine engine;
+    private final TruffleObject delegate;
+
+    public EngineTruffleObject(PolyglotEngine engine, TruffleObject obj) {
+        this.engine = engine;
+        this.delegate = obj;
+    }
+
+    @Override
+    public ForeignAccess getForeignAccess() {
+        return ForeignAccess.create(this);
+    }
+
+    @Override
+    public boolean canHandle(TruffleObject obj) {
+        return true;
+    }
+
+    @Override
+    public CallTarget accessMessage(Message tree) {
+        return Truffle.getRuntime().createCallTarget(new WrappingRoot(TruffleLanguage.class, tree.createNode()));
+    }
+
+    static class WrappingRoot extends RootNode {
+        @Child private Node foreignAccess;
+
+        @SuppressWarnings("rawtypes")
+        public WrappingRoot(Class<? extends TruffleLanguage> lang, Node foreignAccess) {
+            super(lang, null, null);
+            this.foreignAccess = foreignAccess;
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            EngineTruffleObject engineTruffleObject = (EngineTruffleObject) ForeignAccess.getReceiver(frame);
+            try {
+                return engineTruffleObject.engine.invokeForeign(foreignAccess, frame, engineTruffleObject.delegate);
+            } catch (IOException ex) {
+                throw new IllegalArgumentException(ex);
+            }
+        }
+    }
+
+}
--- a/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/JavaWrapper.java	Fri Sep 25 16:36:10 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,76 +0,0 @@
-/*
- * 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.vm;
-
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
-final class JavaWrapper implements InvocationHandler {
-    private final PolyglotEngine.Value value;
-    private final Object wrapper;
-    private final InvocationHandler chain;
-
-    public JavaWrapper(PolyglotEngine.Value value, Object wrapper, InvocationHandler chain) {
-        this.value = value;
-        this.chain = chain;
-        this.wrapper = wrapper;
-    }
-
-    static <T> T create(Class<T> representation, Object wrapper, PolyglotEngine.Value value) {
-        try {
-            InvocationHandler chain = Proxy.getInvocationHandler(wrapper);
-            Object instance = Proxy.newProxyInstance(representation.getClassLoader(), new Class<?>[]{representation}, new JavaWrapper(value, wrapper, chain));
-            return representation.cast(instance);
-        } catch (IllegalArgumentException ex) {
-            return representation.cast(wrapper);
-        }
-    }
-
-    @Override
-    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-        if (method.getDeclaringClass() == Object.class) {
-            return method.invoke(this, args);
-        }
-        PolyglotEngine.Value retValue = value.invokeProxy(chain, wrapper, method, args);
-        if (method.getReturnType() == void.class) {
-            return null;
-        } else {
-            Object realValue = retValue.get();
-            if (realValue == null) {
-                return null;
-            }
-            if (Proxy.isProxyClass(realValue.getClass())) {
-                if (Proxy.getInvocationHandler(realValue) instanceof JavaWrapper) {
-                    return realValue;
-                }
-                final Class<?> type = method.getReturnType();
-                return create(type, realValue, retValue);
-            }
-            return realValue;
-        }
-    }
-
-}
--- a/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/PolyglotEngine.java	Fri Sep 25 16:36:10 2015 +0200
+++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/PolyglotEngine.java	Fri Sep 25 16:53:27 2015 +0200
@@ -32,8 +32,6 @@
 import java.io.OutputStream;
 import java.io.Reader;
 import java.io.Writer;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
 import java.net.URI;
 import java.net.URL;
 import java.net.URLConnection;
@@ -60,11 +58,14 @@
 import com.oracle.truffle.api.debug.Debugger;
 import com.oracle.truffle.api.debug.ExecutionEvent;
 import com.oracle.truffle.api.debug.SuspendedEvent;
+import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.impl.Accessor;
 import com.oracle.truffle.api.instrument.Probe;
 import com.oracle.truffle.api.instrument.ToolSupportProvider;
+import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.interop.java.JavaInterop;
+import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.source.Source;
 
 /**
@@ -530,6 +531,33 @@
         }
     }
 
+    @SuppressWarnings("try")
+    final Object invokeForeign(final Node foreignNode, final VirtualFrame frame, final TruffleObject receiver) throws IOException {
+        final Debugger[] fillIn = {debugger};
+        final Object[] res = {null, null};
+        executor.execute(new Runnable() {
+            @Override
+            public void run() {
+                try (final Closeable c = SPI.executionStart(PolyglotEngine.this, fillIn, null)) {
+                    if (debugger == null) {
+                        debugger = fillIn[0];
+                    }
+                    res[0] = ForeignAccess.execute(foreignNode, frame, receiver, ForeignAccess.getArguments(frame).toArray());
+                } catch (IOException ex) {
+                    res[1] = ex;
+                } catch (Throwable ex) {
+                    res[1] = ex;
+                }
+            }
+        });
+        exceptionCheck(res);
+        if (res[0] instanceof TruffleObject) {
+            return new EngineTruffleObject(this, (TruffleObject) res[0]);
+        } else {
+            return res[0];
+        }
+    }
+
     /**
      * Looks global symbol provided by one of initialized languages up. First of all execute your
      * program via one of your {@link #eval(java.lang.String, java.lang.String)} and then look
@@ -682,7 +710,11 @@
         public Object get() throws IOException {
             waitForSymbol();
             exceptionCheck(result);
-            return result[0];
+            if (result[0] instanceof TruffleObject) {
+                return new EngineTruffleObject(PolyglotEngine.this, (TruffleObject) result[0]);
+            } else {
+                return result[0];
+            }
         }
 
         /**
@@ -702,8 +734,7 @@
             if (representation.isInstance(obj)) {
                 return representation.cast(obj);
             }
-            T wrapper = JavaInterop.asJavaObject(representation, (TruffleObject) obj);
-            return JavaWrapper.create(representation, wrapper, this);
+            return JavaInterop.asJavaObject(representation, (TruffleObject) obj);
         }
 
         /**
@@ -735,32 +766,6 @@
         }
 
         @SuppressWarnings("try")
-        final Value invokeProxy(final InvocationHandler chain, final Object wrapper, final Method method, final Object[] args) throws IOException {
-            final Debugger[] fillIn = {debugger};
-            final CountDownLatch done = new CountDownLatch(1);
-            final Object[] res = {null, null};
-            executor.execute(new Runnable() {
-                @Override
-                public void run() {
-                    try (final Closeable c = SPI.executionStart(PolyglotEngine.this, fillIn, null)) {
-                        if (debugger == null) {
-                            debugger = fillIn[0];
-                        }
-                        res[0] = chain.invoke(wrapper, method, args);
-                    } catch (IOException ex) {
-                        res[1] = ex;
-                    } catch (Throwable ex) {
-                        res[1] = ex;
-                    } finally {
-                        done.countDown();
-                    }
-                }
-            });
-            exceptionCheck(res);
-            return new Value(language, res, done);
-        }
-
-        @SuppressWarnings("try")
         private void invokeImpl(Debugger[] fillIn, Object thiz, Object[] args, Object[] res, CountDownLatch done) {
             try (final Closeable c = SPI.executionStart(PolyglotEngine.this, fillIn, null)) {
                 if (debugger == null) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLJavaInteropTest.java	Fri Sep 25 16:53:27 2015 +0200
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The Universal Permissive License (UPL), Version 1.0
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.oracle.truffle.sl.test;
+
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.java.JavaInterop;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+import java.io.ByteArrayOutputStream;
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+public class SLJavaInteropTest {
+    @Test
+    public void asFunction() throws Exception {
+        String scriptText = "function main() {\n" + "    println(\"Called!\");\n" + "}\n";
+        Source script = Source.fromText(scriptText, "Test").withMimeType("application/x-sl");
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        PolyglotEngine engine = PolyglotEngine.buildNew().setOut(os).build();
+        engine.eval(script);
+        PolyglotEngine.Value main = engine.findGlobalSymbol("main");
+        Runnable runnable = JavaInterop.asJavaFunction(Runnable.class, (TruffleObject) main.get());
+        runnable.run();
+
+        assertEquals("Called!\n", os.toString("UTF-8"));
+    }
+}
--- a/truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/TruffleTCK.java	Fri Sep 25 16:36:10 2015 +0200
+++ b/truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/TruffleTCK.java	Fri Sep 25 16:53:27 2015 +0200
@@ -29,6 +29,7 @@
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.vm.PolyglotEngine;
 import java.io.IOException;
+import java.lang.reflect.Field;
 import java.util.Random;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -520,7 +521,7 @@
         TruffleObject fn = JavaInterop.asTruffleFunction(LongBinaryOperation.class, new MaxMinObject(true));
 
         Object ret = apply.invoke(null, fn).get();
-        assertSame("The same value returned", fn, ret);
+        assertSameTruffleObject("The same value returned", fn, ret);
     }
 
     @Test
@@ -576,6 +577,28 @@
         return obj;
     }
 
+    private static void assertSameTruffleObject(String msg, Object expected, Object actual) {
+        Object unExpected = unwrapTruffleObject(expected);
+        Object unAction = unwrapTruffleObject(actual);
+        assertSame(msg, unExpected, unAction);
+    }
+
+    private static Object unwrapTruffleObject(Object obj) {
+        try {
+            if (obj instanceof TruffleObject) {
+                Class<?> eto = Class.forName("com.oracle.truffle.api.vm.EngineTruffleObject");
+                if (eto.isInstance(obj)) {
+                    final Field field = eto.getDeclaredField("delegate");
+                    field.setAccessible(true);
+                    return field.get(obj);
+                }
+            }
+            return obj;
+        } catch (Exception ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
     interface CompoundObject {
         Number fourtyTwo();