changeset 22294:9f478b9db4f7

Merge with 0e13cbebc04cdabf404d15d2e05aca152880d772
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Thu, 08 Oct 2015 17:00:53 -0400
parents 260e3cdf11ec (current diff) 0e13cbebc04c (diff)
children 7f32031a4812
files truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ImplicitExplicitExportTest.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java
diffstat 30 files changed, 967 insertions(+), 198 deletions(-) [+]
line wrap: on
line diff
--- a/truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/JavaInterop.java	Thu Oct 08 16:48:42 2015 -0400
+++ b/truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/JavaInterop.java	Thu Oct 08 17:00:53 2015 -0400
@@ -135,9 +135,12 @@
      *
      * @param <T> type of requested and returned value
      * @param type interface modeling structure of <code>foreignObject</code> in <b>Java</b>
-     * @param foreignObject object coming from a {@link TruffleObject Truffle language}
+     * @param foreignObject object coming from a {@link TruffleObject Truffle language}, can be
+     *            <code>null</code>, in such case the returned value will likely be
+     *            <code>null</code> as well
      * @return instance of requested interface granting access to specified
-     *         <code>foreignObject</code>
+     *         <code>foreignObject</code>, can be <code>null</code>, if the foreignObject parameter
+     *         was <code>null</code>
      */
     public static <T> T asJavaObject(Class<T> type, TruffleObject foreignObject) {
         return asJavaObject(type, null, foreignObject);
@@ -151,6 +154,9 @@
             if (!clazz.isInterface()) {
                 throw new IllegalArgumentException();
             }
+            if (foreignObject == null) {
+                return null;
+            }
             if (clazz == List.class && Boolean.TRUE.equals(message(Message.HAS_SIZE, foreignObject))) {
                 Class<?> elementType = Object.class;
                 if (type instanceof ParameterizedType) {
--- a/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/Message.java	Thu Oct 08 16:48:42 2015 -0400
+++ b/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/Message.java	Thu Oct 08 17:00:53 2015 -0400
@@ -413,7 +413,11 @@
                 return (Message) Message.class.getMethod(factory, int.class).invoke(null, 0);
             } catch (Exception ex2) {
                 try {
-                    return (Message) Class.forName(message).newInstance();
+                    ClassLoader l = Message.class.getClassLoader();
+                    if (l == null) {
+                        l = ClassLoader.getSystemClassLoader();
+                    }
+                    return (Message) Class.forName(message, false, l).newInstance();
                 } catch (Exception ex1) {
                     throw new IllegalArgumentException("Cannot find message for " + message, ex);
                 }
--- a/truffle/com.oracle.truffle.api.object/src/com/oracle/truffle/api/object/Layout.java	Thu Oct 08 16:48:42 2015 -0400
+++ b/truffle/com.oracle.truffle.api.object/src/com/oracle/truffle/api/object/Layout.java	Thu Oct 08 17:00:53 2015 -0400
@@ -35,9 +35,9 @@
  * An object may change its shape but only to shapes of the same layout.
  */
 public abstract class Layout {
-    public static final EnumSet<ImplicitCast> NONE = EnumSet.noneOf(ImplicitCast.class);
-    public static final EnumSet<ImplicitCast> INT_TO_DOUBLE = EnumSet.of(ImplicitCast.IntToDouble);
-    public static final EnumSet<ImplicitCast> INT_TO_LONG = EnumSet.of(ImplicitCast.IntToLong);
+    @Deprecated public static final EnumSet<ImplicitCast> NONE = EnumSet.noneOf(ImplicitCast.class);
+    @Deprecated public static final EnumSet<ImplicitCast> INT_TO_DOUBLE = EnumSet.of(ImplicitCast.IntToDouble);
+    @Deprecated public static final EnumSet<ImplicitCast> INT_TO_LONG = EnumSet.of(ImplicitCast.IntToLong);
 
     public static final String OPTION_PREFIX = "truffle.object.";
 
@@ -62,7 +62,7 @@
      * Equivalent to {@code Layout.newLayout().build()}.
      */
     public static Layout createLayout() {
-        return createLayout(NONE);
+        return newLayout().build();
     }
 
     /**
@@ -139,7 +139,7 @@
          * Create a new layout builder.
          */
         private Builder() {
-            this.allowedImplicitCasts = Layout.NONE;
+            this.allowedImplicitCasts = EnumSet.noneOf(ImplicitCast.class);
         }
 
         /**
@@ -159,6 +159,16 @@
             return this;
         }
 
+        /**
+         * Add an allowed implicit cast in this layout.
+         *
+         * @see Layout.ImplicitCast
+         */
+        public Builder addAllowedImplicitCast(ImplicitCast allowedImplicitCast) {
+            this.allowedImplicitCasts.add(allowedImplicitCast);
+            return this;
+        }
+
         public EnumSet<ImplicitCast> getAllowedImplicitCasts() {
             return allowedImplicitCasts;
         }
--- a/truffle/com.oracle.truffle.api.object/src/com/oracle/truffle/api/object/Shape.java	Thu Oct 08 16:48:42 2015 -0400
+++ b/truffle/com.oracle.truffle.api.object/src/com/oracle/truffle/api/object/Shape.java	Thu Oct 08 17:00:53 2015 -0400
@@ -25,6 +25,7 @@
 package com.oracle.truffle.api.object;
 
 import com.oracle.truffle.api.Assumption;
+
 import java.util.EnumSet;
 import java.util.List;
 
@@ -57,6 +58,20 @@
     public abstract Shape addProperty(Property property);
 
     /**
+     * Add or change property in the map, yielding a new or cached Shape object.
+     *
+     * @return the shape after defining the property
+     */
+    public abstract Shape defineProperty(Object key, Object value, int flags);
+
+    /**
+     * Add or change property in the map, yielding a new or cached Shape object.
+     *
+     * @return the shape after defining the property
+     */
+    public abstract Shape defineProperty(Object key, Object value, int flags, LocationFactory locationFactory);
+
+    /**
      * An {@link Iterable} over the shape's properties in insertion order.
      */
     public abstract Iterable<Property> getProperties();
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/interop/MessageStringTest.java	Thu Oct 08 16:48:42 2015 -0400
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/interop/MessageStringTest.java	Thu Oct 08 17:00:53 2015 -0400
@@ -31,7 +31,6 @@
 import java.lang.reflect.Modifier;
 import java.util.Locale;
 
-import org.junit.Ignore;
 import org.junit.Test;
 
 import com.oracle.truffle.api.interop.Message;
@@ -88,7 +87,6 @@
         }
     }
 
-    @Ignore
     @Test
     public void specialMessagePersitance() {
         SpecialMsg msg = new SpecialMsg();
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/source/SourceSectionTest.java	Thu Oct 08 16:48:42 2015 -0400
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/source/SourceSectionTest.java	Thu Oct 08 17:00:53 2015 -0400
@@ -22,12 +22,13 @@
  */
 package com.oracle.truffle.api.test.source;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.source.SourceSection;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import org.junit.Ignore;
-import org.junit.Test;
 
 public class SourceSectionTest {
 
@@ -56,14 +57,6 @@
         assertEquals(section.getStartColumn(), 1);
     }
 
-    @Ignore
-    @Test
-    public void emptyLineTest0a() {
-        SourceSection section = emptyLineSource.createSection("test", 0, 0);
-        assertEquals(section.getEndLine(), 1);
-        assertEquals(section.getEndColumn(), 1);
-    }
-
     @Test
     public void emptyLineTest1() {
         SourceSection section = emptyLineSource.createSection("test", 0, 1);
@@ -77,20 +70,6 @@
         assertEquals(section.getEndColumn(), 1);
     }
 
-    @Ignore
-    @Test
-    public void emptyLineTest2() {
-        SourceSection section = emptyLineSource.createSection("test", 1, 0);
-        assertNotNull(section);
-        assertEquals(section.getCode(), "");
-        assertEquals(section.getCharIndex(), 1);
-        assertEquals(section.getCharLength(), 0);
-        assertEquals(section.getStartLine(), 1);
-        assertEquals(section.getStartColumn(), 1);
-        assertEquals(section.getEndLine(), 1);
-        assertEquals(section.getEndColumn(), 1);
-    }
-
     @Test
     public void emptySectionTest2() {
         SourceSection section = shortSource.createSection("test", 0, 0);
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/AccessorTest.java	Thu Oct 08 16:48:42 2015 -0400
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/AccessorTest.java	Thu Oct 08 17:00:53 2015 -0400
@@ -54,8 +54,8 @@
         PolyglotEngine.Language language = vm.getLanguages().get(L1);
         assertNotNull("L1 language is defined", language);
 
-        Source s = Source.fromText("return nothing", "nothing").withMimeType(L1);
-        Object ret = vm.eval(s).get();
+        Source s = Source.fromText("return nothing", "nothing");
+        Object ret = language.eval(s).get();
         assertNull("nothing is returned", ret);
 
         Object afterInitialization = findLanguageByClass(vm);
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ImplicitExplicitExportTest.java	Thu Oct 08 16:48:42 2015 -0400
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ImplicitExplicitExportTest.java	Thu Oct 08 17:00:53 2015 -0400
@@ -51,6 +51,7 @@
 import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.vm.PolyglotEngine;
+import java.util.Objects;
 
 public class ImplicitExplicitExportTest {
     private static Thread mainThread;
@@ -268,6 +269,19 @@
 
         public ExportImportLanguage1() {
         }
+
+        @Override
+        protected String toString(Ctx ctx, Object value) {
+            if (value instanceof String) {
+                try {
+                    int number = Integer.parseInt((String) value);
+                    return number + ": Int";
+                } catch (NumberFormatException ex) {
+                    // go on
+                }
+            }
+            return Objects.toString(value);
+        }
     }
 
     @TruffleLanguage.Registration(mimeType = L2, name = "ImportExport2", version = "0")
@@ -276,6 +290,19 @@
 
         public ExportImportLanguage2() {
         }
+
+        @Override
+        protected String toString(Ctx ctx, Object value) {
+            if (value instanceof String) {
+                try {
+                    double number = Double.parseDouble((String) value);
+                    return number + ": Double";
+                } catch (NumberFormatException ex) {
+                    // go on
+                }
+            }
+            return Objects.toString(value);
+        }
     }
 
     @TruffleLanguage.Registration(mimeType = L3, name = "ImportExport3", version = "0")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ToStringTest.java	Thu Oct 08 17:00:53 2015 -0400
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ * 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.test.vm;
+
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+public class ToStringTest {
+    @Test
+    public void valueToStringValueWith1() throws Exception {
+        PolyglotEngine engine = PolyglotEngine.buildNew().build();
+        PolyglotEngine.Language language1 = engine.getLanguages().get("application/x-test-import-export-1");
+        PolyglotEngine.Language language2 = engine.getLanguages().get("application/x-test-import-export-2");
+        language2.eval(Source.fromText("explicit.value=42", "define 42"));
+        PolyglotEngine.Value value = language1.eval(Source.fromText("return=value", "42.value"));
+        assertEquals("It's fourtytwo", "42", value.get());
+
+        String textual = value.as(String.class);
+        assertEquals("Nicely formated as by L1", "42: Int", textual);
+    }
+
+    @Test
+    public void valueToStringValueWith2() throws Exception {
+        PolyglotEngine engine = PolyglotEngine.buildNew().build();
+        PolyglotEngine.Language language1 = engine.getLanguages().get("application/x-test-import-export-1");
+        PolyglotEngine.Language language2 = engine.getLanguages().get("application/x-test-import-export-2");
+        language1.eval(Source.fromText("explicit.value=42", "define 42"));
+        PolyglotEngine.Value value = language2.eval(Source.fromText("return=value", "42.value"));
+        assertEquals("It's fourtytwo", "42", value.get());
+
+        String textual = value.as(String.class);
+        assertEquals("Nicely formated as by L2", "42.0: Double", textual);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ValueTest.java	Thu Oct 08 17:00:53 2015 -0400
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ * 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.test.vm;
+
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.Executor;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+
+public class ValueTest implements Executor {
+    private List<Runnable> pending = new LinkedList<>();
+
+    @Test
+    public void valueToStringValue() throws Exception {
+        PolyglotEngine engine = PolyglotEngine.buildNew().build();
+        PolyglotEngine.Language language1 = engine.getLanguages().get("application/x-test-import-export-1");
+        PolyglotEngine.Language language2 = engine.getLanguages().get("application/x-test-import-export-2");
+        language2.eval(Source.fromText("explicit.value=42", "define 42"));
+        PolyglotEngine.Value value = language1.eval(Source.fromText("return=value", "42.value"));
+        assertEquals("It's fourtytwo", "42", value.get());
+
+        String textual = value.toString();
+        assertTrue("Contains the value " + textual, textual.contains("value=42"));
+        assertTrue("Is computed " + textual, textual.contains("computed=true"));
+        assertTrue("No error " + textual, textual.contains("exception=null"));
+    }
+
+    @Test
+    public void valueToStringException() throws Exception {
+        PolyglotEngine engine = PolyglotEngine.buildNew().build();
+        PolyglotEngine.Language language1 = engine.getLanguages().get("application/x-test-import-export-1");
+        PolyglotEngine.Value value = null;
+        try {
+            value = language1.eval(Source.fromText("parse=does not work", "error.value"));
+            Object res = value.get();
+            fail("Should throw an exception: " + res);
+        } catch (IOException ex) {
+            assertTrue("Message contains the right text: " + ex.getMessage(), ex.getMessage().contains("does not work"));
+        }
+
+        assertNull("No value returned", value);
+    }
+
+    @Test
+    public void valueToStringValueAsync() throws Exception {
+        PolyglotEngine engine = PolyglotEngine.buildNew().executor(this).build();
+        PolyglotEngine.Language language1 = engine.getLanguages().get("application/x-test-import-export-1");
+        PolyglotEngine.Language language2 = engine.getLanguages().get("application/x-test-import-export-2");
+        language2.eval(Source.fromText("explicit.value=42", "define 42"));
+        flush();
+
+        PolyglotEngine.Value value = language1.eval(Source.fromText("return=value", "42.value"));
+
+        String textual = value.toString();
+        assertFalse("Doesn't contain the value " + textual, textual.contains("value=42"));
+        assertTrue("Is not computed " + textual, textual.contains("computed=false"));
+        assertTrue("No error " + textual, textual.contains("exception=null"));
+        assertTrue("No value yet " + textual, textual.contains("value=null"));
+
+        flush();
+
+        textual = value.toString();
+        assertTrue("Is computed " + textual, textual.contains("computed=true"));
+        assertTrue("No error " + textual, textual.contains("exception=null"));
+        assertTrue("value computed " + textual, textual.contains("value=42"));
+    }
+
+    @Test
+    public void valueToStringExceptionAsync() throws Exception {
+        PolyglotEngine engine = PolyglotEngine.buildNew().executor(this).build();
+        PolyglotEngine.Language language1 = engine.getLanguages().get("application/x-test-import-export-1");
+        PolyglotEngine.Value value = language1.eval(Source.fromText("parse=does not work", "error.value"));
+        assertNotNull("Value returned", value);
+
+        String textual = value.toString();
+        assertTrue("Is not computed " + textual, textual.contains("computed=false"));
+        assertTrue("No error " + textual, textual.contains("exception=null"));
+        assertTrue("No value yet " + textual, textual.contains("value=null"));
+
+        flush();
+
+        textual = value.toString();
+        assertTrue("Is computed " + textual, textual.contains("computed=true"));
+        assertTrue("No value at all" + textual, textual.contains("value=null"));
+        assertTrue("Error " + textual, textual.contains("exception=java.io.IOException: does not work"));
+    }
+
+    @Override
+    public void execute(Runnable command) {
+        pending.add(command);
+    }
+
+    private void flush() {
+        for (Runnable r : pending) {
+            r.run();
+        }
+    }
+}
--- a/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/EngineTruffleObject.java	Thu Oct 08 16:48:42 2015 -0400
+++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/EngineTruffleObject.java	Thu Oct 08 17:00:53 2015 -0400
@@ -34,6 +34,7 @@
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.RootNode;
 import java.io.IOException;
+import java.util.Objects;
 
 final class EngineTruffleObject implements TruffleObject, ForeignAccess.Factory {
     private final PolyglotEngine engine;
@@ -63,6 +64,31 @@
         return Truffle.getRuntime().createCallTarget(new WrappingRoot(TruffleLanguage.class, tree.createNode()));
     }
 
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 89 * hash + Objects.hashCode(this.engine);
+        hash = 89 * hash + Objects.hashCode(this.delegate);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof EngineTruffleObject) {
+            final EngineTruffleObject other = (EngineTruffleObject) obj;
+            return engine == other.engine && Objects.equals(this.delegate, other.delegate);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return delegate.toString();
+    }
+
     static class WrappingRoot extends RootNode {
         @Child private Node foreignAccess;
 
--- a/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/PolyglotEngine.java	Thu Oct 08 16:48:42 2015 -0400
+++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/PolyglotEngine.java	Thu Oct 08 17:00:53 2015 -0400
@@ -510,7 +510,7 @@
 
     @SuppressWarnings("try")
     private void evalImpl(TruffleLanguage<?>[] fillLang, Source s, Object[] result, Language l, CountDownLatch ready) {
-        try (Closeable d = SPI.executionStart(this, debugger, s)) {
+        try (Closeable d = SPI.executionStart(this, -1, debugger, s)) {
             TruffleLanguage<?> langImpl = l.getImpl(true);
             fillLang[0] = langImpl;
             result[0] = SPI.eval(langImpl, s);
@@ -527,7 +527,7 @@
         executor.execute(new Runnable() {
             @Override
             public void run() {
-                try (final Closeable c = SPI.executionStart(PolyglotEngine.this, debugger, null)) {
+                try (final Closeable c = SPI.executionStart(PolyglotEngine.this, -1, debugger, null)) {
                     res[0] = ForeignAccess.execute(foreignNode, frame, receiver, ForeignAccess.getArguments(frame).toArray());
                 } catch (IOException ex) {
                     res[1] = ex;
@@ -707,7 +707,10 @@
          * Obtains Java view of the object represented by this symbol. The method basically
          * delegates to
          * {@link JavaInterop#asJavaObject(java.lang.Class, com.oracle.truffle.api.interop.TruffleObject)}
-         * just handles primitive types as well.
+         * . The method handles primitive types (like {@link Number}, etc.) by casting and returning
+         * them. When a {@link String}.<code>class</code> is requested, the method let's the
+         * language that produced the value to do the
+         * {@link TruffleLanguage#toString(java.lang.Object, java.lang.Object) necessary formating}.
          *
          * @param <T> the type of the view one wants to obtain
          * @param representation the class of the view interface (it has to be an interface)
@@ -723,6 +726,10 @@
                     return representation.cast(eto.getDelegate());
                 }
             }
+            if (representation == String.class) {
+                final Class<? extends TruffleLanguage> clazz = language.getClass();
+                return representation.cast(SPI.toString(language, findEnv(clazz), obj));
+            }
             if (representation.isInstance(obj)) {
                 return representation.cast(obj);
             }
@@ -758,7 +765,7 @@
 
         @SuppressWarnings("try")
         private void invokeImpl(Object thiz, Object[] args, Object[] res, CountDownLatch done) {
-            try (final Closeable c = SPI.executionStart(PolyglotEngine.this, debugger, null)) {
+            try (final Closeable c = SPI.executionStart(PolyglotEngine.this, -1, debugger, null)) {
                 List<Object> arr = new ArrayList<>();
                 if (thiz == null) {
                     if (language != null) {
@@ -794,11 +801,18 @@
         private void waitForSymbol() throws InterruptedIOException {
             checkThread();
             try {
-                ready.await();
+                if (ready != null) {
+                    ready.await();
+                }
             } catch (InterruptedException ex) {
                 throw (InterruptedIOException) new InterruptedIOException(ex.getMessage()).initCause(ex);
             }
         }
+
+        @Override
+        public String toString() {
+            return "PolyglotEngine.Value[value=" + result[0] + ",exception=" + result[1] + ",computed=" + (ready.getCount() == 0) + "]";
+        }
     }
 
     /**
@@ -847,10 +861,38 @@
         }
 
         /**
-         * Human readable string that identifies the language and version.
+         * Evaluates provided source. Ignores the particular {@link Source#getMimeType() MIME type}
+         * and forces evaluation in the context of <code>this</code> language.
+         * 
+         * @param source code snippet to execute
+         * @return a {@link Value} object that holds result of an execution, never <code>null</code>
+         * @throws IOException thrown to signal errors while processing the code
+         */
+        public Value eval(Source source) throws IOException {
+            checkThread();
+            return PolyglotEngine.this.eval(this, source);
+        }
+
+        /**
+         * Returns value representing global object of the language.
+         * <p>
+         * The object is expected to be <code>TruffleObject</code> (e.g. a native object from the
+         * other language) but technically it can be one of Java primitive wrappers ({@link Integer}, {@link Double}, {@link Short}, etc.).
          *
-         * @return string describing the specific language version
+         * @return the global object or <code>null</code> if the language does not support such
+         *         concept
          */
+        public Value getGlobalObject() {
+            checkThread();
+
+            Object[] res = {SPI.languageGlobal(getEnv(true)), null};
+            return res[0] == null ? null : new Value(info.getImpl(true), res, null);
+        }
+
+        /**
+         * @deprecated concatenate {@link #getName()} and {@link #getVersion()} the way you want.
+         */
+        @Deprecated
         public String getShortName() {
             return getName() + "(" + getVersion() + ")";
         }
@@ -993,9 +1035,9 @@
         }
 
         @Override
-        protected Closeable executionStart(Object obj, Debugger debugger, Source s) {
+        protected Closeable executionStart(Object obj, int currentDepth, Debugger debugger, Source s) {
             PolyglotEngine vm = (PolyglotEngine) obj;
-            return super.executionStart(vm, debugger, s);
+            return super.executionStart(vm, -1, debugger, s);
         }
 
         @Override
@@ -1008,5 +1050,10 @@
         protected void dispose(TruffleLanguage<?> impl, TruffleLanguage.Env env) {
             super.dispose(impl, env);
         }
+
+        @Override
+        protected String toString(TruffleLanguage language, TruffleLanguage.Env env, Object obj) {
+            return super.toString(language, env, obj);
+        }
     } // end of SPIAccessor
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java	Thu Oct 08 16:48:42 2015 -0400
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java	Thu Oct 08 17:00:53 2015 -0400
@@ -53,6 +53,7 @@
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.api.source.Source;
+import java.util.Objects;
 
 /**
  * An entry point for everyone who wants to implement a Truffle based language. By providing an
@@ -75,6 +76,8 @@
  */
 @SuppressWarnings("javadoc")
 public abstract class TruffleLanguage<C> {
+    private final Map<Source, CallTarget> compiled = Collections.synchronizedMap(new WeakHashMap<Source, CallTarget>());
+
     /**
      * Constructor to be called by subclasses.
      */
@@ -266,6 +269,23 @@
     protected abstract Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException;
 
     /**
+     * Generates language specific textual representation of a value. Each language may have special
+     * formating conventions - even primitive values may not follow the traditional Java formating
+     * rules. As such when
+     * {@link com.oracle.truffle.api.vm.PolyglotEngine.Value#as(java.lang.Class)
+     * value.as(String.class)} is requested, it consults the language that produced the value by
+     * calling this method. By default this method calls {@link Objects#toString(java.lang.Object)}.
+     *
+     * @param context the execution context for doing the conversion
+     * @param value the value to convert. Either primitive type or
+     *            {@link com.oracle.truffle.api.interop.TruffleObject}
+     * @return textual representation of the value in this language
+     */
+    protected String toString(C context, Object value) {
+        return Objects.toString(value);
+    }
+
+    /**
      * Allows a language implementor to create a node that can effectively lookup up the context
      * associated with current execution. The context is created by
      * {@link #createContext(com.oracle.truffle.api.TruffleLanguage.Env)} method.
@@ -323,6 +343,11 @@
         void dispose() {
             lang.disposeContext(ctx);
         }
+
+        String toString(TruffleLanguage<?> language, Object obj) {
+            assert lang == language;
+            return lang.toString(ctx, obj);
+        }
     }
 
     /**
@@ -428,8 +453,6 @@
             return super.importSymbol(vm, queryingLang, globalName);
         }
 
-        private static final Map<Source, CallTarget> COMPILED = Collections.synchronizedMap(new WeakHashMap<Source, CallTarget>());
-
         @Override
         protected CallTarget parse(TruffleLanguage<?> truffleLanguage, Source code, Node context, String... argumentNames) throws IOException {
             return truffleLanguage.parse(code, context, argumentNames);
@@ -437,13 +460,13 @@
 
         @Override
         protected Object eval(TruffleLanguage<?> language, Source source) throws IOException {
-            CallTarget target = COMPILED.get(source);
+            CallTarget target = language.compiled.get(source);
             if (target == null) {
                 target = language.parse(source, null);
                 if (target == null) {
                     throw new IOException("Parsing has not produced a CallTarget for " + source);
                 }
-                COMPILED.put(source, target);
+                language.compiled.put(source, target);
             }
             try {
                 return target.call();
@@ -504,6 +527,11 @@
             assert impl == env.langCtx.lang;
             env.langCtx.dispose();
         }
+
+        @Override
+        protected String toString(TruffleLanguage<?> language, Env env, Object obj) {
+            return env.langCtx.toString(language, obj);
+        }
     }
 
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java	Thu Oct 08 16:48:42 2015 -0400
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java	Thu Oct 08 17:00:53 2015 -0400
@@ -118,8 +118,8 @@
         Source.setFileCaching(true);
 
         // Initialize execution context stack
-        debugContext = new DebugExecutionContext(null, null);
-        prepareContinue();
+        debugContext = new DebugExecutionContext(null, null, 0);
+        debugContext.setStrategy(0, new Continue());
         debugContext.contextTrace("START EXEC DEFAULT");
 
         breakpointCallback = new BreakpointCallback() {
@@ -194,8 +194,8 @@
      * </ul>
      */
     @TruffleBoundary
-    void prepareContinue() {
-        debugContext.setStrategy(new Continue());
+    void prepareContinue(int depth) {
+        debugContext.setStrategy(depth, new Continue());
     }
 
     /**
@@ -679,12 +679,16 @@
         private MaterializedFrame haltedFrame;
 
         private DebugExecutionContext(Source executionSource, DebugExecutionContext previousContext) {
+            this(executionSource, previousContext, -1);
+        }
+
+        private DebugExecutionContext(Source executionSource, DebugExecutionContext previousContext, int depth) {
             this.source = executionSource;
             this.predecessor = previousContext;
             this.level = previousContext == null ? 0 : previousContext.level + 1;
 
             // "Base" is the number of stack frames for all nested (halted) executions.
-            this.contextStackBase = currentStackDepth();
+            this.contextStackBase = depth == -1 ? currentStackDepth() : depth;
             this.running = true;
             contextTrace("NEW CONTEXT");
         }
@@ -695,9 +699,13 @@
          * @param stepStrategy
          */
         void setStrategy(StepStrategy stepStrategy) {
+            setStrategy(currentStackDepth(), stepStrategy);
+        }
+
+        void setStrategy(int depth, StepStrategy stepStrategy) {
             if (this.strategy == null) {
                 this.strategy = stepStrategy;
-                this.strategy.enable(this, currentStackDepth());
+                this.strategy.enable(this, depth);
                 if (TRACE) {
                     contextTrace("SET MODE <none>-->" + stepStrategy.getName());
                 }
@@ -809,7 +817,7 @@
 
     }
 
-    void executionStarted(Source source) {
+    void executionStarted(int depth, Source source) {
         Source execSource = source;
         if (execSource == null) {
             execSource = lastSource;
@@ -817,8 +825,8 @@
             lastSource = execSource;
         }
         // Push a new execution context onto stack
-        debugContext = new DebugExecutionContext(execSource, debugContext);
-        prepareContinue();
+        debugContext = new DebugExecutionContext(execSource, debugContext, depth);
+        prepareContinue(depth);
         debugContext.contextTrace("START EXEC ");
         ACCESSOR.dispatchEvent(vm, new ExecutionEvent(this));
     }
@@ -840,8 +848,8 @@
     static final class AccessorDebug extends Accessor {
 
         @Override
-        protected Closeable executionStart(Object vm, final Debugger debugger, Source s) {
-            debugger.executionStarted(s);
+        protected Closeable executionStart(Object vm, int currentDepth, final Debugger debugger, Source s) {
+            debugger.executionStarted(currentDepth, s);
             return new Closeable() {
                 @Override
                 public void close() throws IOException {
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/ExecutionEvent.java	Thu Oct 08 16:48:42 2015 -0400
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/ExecutionEvent.java	Thu Oct 08 17:00:53 2015 -0400
@@ -70,7 +70,7 @@
      * </ul>
      */
     public void prepareContinue() {
-        debugger.prepareContinue();
+        debugger.prepareContinue(-1);
     }
 
     /**
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/SuspendedEvent.java	Thu Oct 08 16:48:42 2015 -0400
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/SuspendedEvent.java	Thu Oct 08 17:00:53 2015 -0400
@@ -128,7 +128,7 @@
      * </ul>
      */
     public void prepareContinue() {
-        debugger.prepareContinue();
+        debugger.prepareContinue(-1);
     }
 
     /**
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java	Thu Oct 08 16:48:42 2015 -0400
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java	Thu Oct 08 17:00:53 2015 -0400
@@ -267,10 +267,11 @@
     private static Reference<Object> previousVM = new WeakReference<>(null);
     private static Assumption oneVM = Truffle.getRuntime().createAssumption();
 
-    protected Closeable executionStart(Object vm, Debugger debugger, Source s) {
+    @SuppressWarnings("unused")
+    protected Closeable executionStart(Object vm, int currentDepth, Debugger debugger, Source s) {
         vm.getClass();
-        final Closeable debugClose = DEBUG.executionStart(vm, debugger, s);
         final Object prev = CURRENT_VM.get();
+        final Closeable debugClose = DEBUG.executionStart(vm, prev == null ? 0 : -1, debugger, s);
         if (!(vm == previousVM.get())) {
             previousVM = new WeakReference<>(vm);
             oneVM.invalidate();
@@ -338,4 +339,7 @@
         return API.parse(truffleLanguage, code, context, argumentNames);
     }
 
+    protected String toString(TruffleLanguage<?> language, Env env, Object obj) {
+        return API.toString(language, env, obj);
+    }
 }
--- a/truffle/com.oracle.truffle.object.basic/src/com/oracle/truffle/object/basic/DefaultStrategy.java	Thu Oct 08 16:48:42 2015 -0400
+++ b/truffle/com.oracle.truffle.object.basic/src/com/oracle/truffle/object/basic/DefaultStrategy.java	Thu Oct 08 17:00:53 2015 -0400
@@ -22,8 +22,9 @@
  */
 package com.oracle.truffle.object.basic;
 
+import java.util.Objects;
+
 import com.oracle.truffle.api.object.DynamicObject;
-import com.oracle.truffle.api.object.Layout;
 import com.oracle.truffle.api.object.Location;
 import com.oracle.truffle.api.object.Property;
 import com.oracle.truffle.api.object.Shape;
@@ -32,61 +33,56 @@
 import com.oracle.truffle.object.LocationImpl;
 import com.oracle.truffle.object.ShapeImpl;
 import com.oracle.truffle.object.ShapeImpl.BaseAllocator;
-import java.util.Objects;
 
-class DefaultStrategy implements LayoutStrategy {
+class DefaultStrategy extends LayoutStrategy {
+    @Override
     public boolean updateShape(DynamicObject object) {
         assert object.getShape().isValid();
         return false;
     }
 
-    public Shape returnCached(Shape newShape) {
+    @Override
+    public ShapeImpl ensureValid(ShapeImpl newShape) {
         assert newShape.isValid();
         return newShape;
     }
 
-    private static boolean assertLocationInRange(Shape shape, Location location) {
+    private static boolean assertLocationInRange(ShapeImpl shape, Location location) {
         BasicLayout layout = (BasicLayout) shape.getLayout();
-        assert (((ShapeImpl) shape).getPrimitiveFieldSize() + ((LocationImpl) location).primitiveFieldCount() <= layout.getPrimitiveFieldCount());
-        assert (((ShapeImpl) shape).getObjectFieldSize() + ((LocationImpl) location).objectFieldCount() <= layout.getObjectFieldCount());
+        assert (shape.getPrimitiveFieldSize() + ((LocationImpl) location).primitiveFieldCount() <= layout.getPrimitiveFieldCount());
+        assert (shape.getObjectFieldSize() + ((LocationImpl) location).objectFieldCount() <= layout.getObjectFieldCount());
         return true;
     }
 
-    public Shape ensureSpace(Shape shape, Location location) {
+    @Override
+    public ShapeImpl ensureSpace(ShapeImpl shape, Location location) {
         Objects.requireNonNull(location);
         assert assertLocationInRange(shape, location);
         return shape;
     }
 
+    @Override
     public boolean isAutoExtArray() {
         return false;
     }
 
-    public Property generalizeProperty(DynamicObject object, Property oldProperty, Object value) {
-        Shape oldShape = object.getShape();
-        Location oldLocation = oldProperty.getLocation();
-        Location newLocation = ((BasicAllocator) oldShape.allocator()).locationForValueUpcast(value, oldLocation);
-        Property newProperty = oldProperty.relocate(newLocation);
-        Shape newShape = oldShape.replaceProperty(oldProperty, newProperty);
-        newProperty.setSafe(object, value, oldShape, newShape);
-        return newProperty;
-    }
-
-    public Property generalizeProperty(DynamicObject object, Property oldProperty, Object value, Shape currentShape, Shape oldNewShape) {
+    @Override
+    public ShapeAndProperty generalizeProperty(Property oldProperty, Object value, ShapeImpl currentShape, ShapeImpl nextShape) {
         Location oldLocation = oldProperty.getLocation();
         Location newLocation = ((BasicAllocator) currentShape.allocator()).locationForValueUpcast(value, oldLocation);
         Property newProperty = oldProperty.relocate(newLocation);
-        Shape newShape = oldNewShape.replaceProperty(oldProperty, newProperty);
-        newProperty.setSafe(object, value, currentShape, newShape);
-        return newProperty;
+        Shape newShape = nextShape.replaceProperty(oldProperty, newProperty);
+        return new ShapeAndProperty(newShape, newProperty);
     }
 
-    public BaseAllocator createAllocator(Shape shape) {
-        return new DefaultAllocatorImpl((ShapeImpl) shape);
+    @Override
+    public BaseAllocator createAllocator(ShapeImpl shape) {
+        return new DefaultAllocatorImpl(shape);
     }
 
-    public BaseAllocator createAllocator(Layout layout) {
-        return new DefaultAllocatorImpl((LayoutImpl) layout);
+    @Override
+    public BaseAllocator createAllocator(LayoutImpl layout) {
+        return new DefaultAllocatorImpl(layout);
     }
 
     public static class DefaultAllocatorImpl extends BasicAllocator {
--- a/truffle/com.oracle.truffle.object/src/com/oracle/truffle/object/DebugCounter.java	Thu Oct 08 16:48:42 2015 -0400
+++ b/truffle/com.oracle.truffle.object/src/com/oracle/truffle/object/DebugCounter.java	Thu Oct 08 17:00:53 2015 -0400
@@ -26,42 +26,75 @@
 import java.util.ArrayList;
 import java.util.concurrent.atomic.AtomicLong;
 
-public final class DebugCounter {
-    private static final ArrayList<DebugCounter> allCounters = new ArrayList<>();
-
-    private final String name;
-    private final AtomicLong value;
+public abstract class DebugCounter {
+    private DebugCounter() {
+    }
 
-    private DebugCounter(String name) {
-        this.name = name;
-        this.value = new AtomicLong();
-        allCounters.add(this);
-    }
+    public abstract long get();
+
+    public abstract void inc();
 
     public static DebugCounter create(String name) {
-        return new DebugCounter(name);
-    }
-
-    public long get() {
-        return value.get();
-    }
-
-    public void inc() {
-        value.incrementAndGet();
-    }
-
-    @Override
-    public String toString() {
-        return name + ": " + value;
+        return ObjectStorageOptions.DebugCounters ? DebugCounterImpl.createImpl(name) : Dummy.INSTANCE;
     }
 
     public static void dumpCounters() {
-        dumpCounters(System.out);
+        if (ObjectStorageOptions.DebugCounters) {
+            DebugCounterImpl.dumpCounters(System.out);
+        }
     }
 
-    public static void dumpCounters(PrintStream out) {
-        for (DebugCounter counter : allCounters) {
-            out.println(counter);
+    private static final class DebugCounterImpl extends DebugCounter {
+        private static final ArrayList<DebugCounter> allCounters = new ArrayList<>();
+
+        private final String name;
+        private final AtomicLong value;
+
+        private DebugCounterImpl(String name) {
+            this.name = name;
+            this.value = new AtomicLong();
+            allCounters.add(this);
+        }
+
+        private static DebugCounter createImpl(String name) {
+            return new DebugCounterImpl(name);
+        }
+
+        @Override
+        public long get() {
+            return value.get();
+        }
+
+        @Override
+        public void inc() {
+            value.incrementAndGet();
+        }
+
+        @Override
+        public String toString() {
+            return name + ": " + get();
+        }
+
+        private static void dumpCounters(PrintStream out) {
+            for (DebugCounter counter : allCounters) {
+                out.println(counter);
+            }
+        }
+    }
+
+    private static final class Dummy extends DebugCounter {
+        static final DebugCounter INSTANCE = new Dummy();
+
+        private Dummy() {
+        }
+
+        @Override
+        public long get() {
+            return 0;
+        }
+
+        @Override
+        public void inc() {
         }
     }
 }
--- a/truffle/com.oracle.truffle.object/src/com/oracle/truffle/object/DynamicObjectImpl.java	Thu Oct 08 16:48:42 2015 -0400
+++ b/truffle/com.oracle.truffle.object/src/com/oracle/truffle/object/DynamicObjectImpl.java	Thu Oct 08 17:00:53 2015 -0400
@@ -283,43 +283,23 @@
     @Override
     @TruffleBoundary
     public void define(Object id, Object value, int flags) {
-        ShapeImpl oldShape = getShape();
-        Property existing = oldShape.getProperty(id);
-        if (existing == null) {
-            updateShape();
-            oldShape = getShape();
-            Shape newShape = oldShape.addProperty(Property.create(id, oldShape.allocator().locationForValue(value, true, value != null), flags));
-            updateShape();
-            newShape.getLastProperty().setGeneric(this, value, oldShape, newShape);
-        } else {
-            defineExisting(id, value, flags, existing, oldShape);
-        }
-    }
-
-    private void defineExisting(Object id, Object value, int flags, Property existing, ShapeImpl oldShape) {
-        if (existing.getFlags() == flags) {
-            existing.setGeneric(this, value, null);
-        } else {
-            Property newProperty = Property.create(id, oldShape.getLayout().existingLocationForValue(value, existing.getLocation(), oldShape), flags);
-            Shape newShape = oldShape.replaceProperty(existing, newProperty);
-            this.setShapeAndResize(newShape);
-            newProperty.setInternal(this, value);
-        }
+        define(id, value, flags, ShapeImpl.DEFAULT_LAYOUT_FACTORY);
     }
 
     @Override
     @TruffleBoundary
     public void define(Object id, Object value, int flags, LocationFactory locationFactory) {
         ShapeImpl oldShape = getShape();
-        Property existing = oldShape.getProperty(id);
-        if (existing == null) {
-            updateShape();
+        ShapeImpl newShape = oldShape.defineProperty(id, value, flags, locationFactory);
+        if (updateShape()) {
             oldShape = getShape();
-            Shape newShape = oldShape.addProperty(Property.create(id, locationFactory.createLocation(oldShape, value), flags));
-            updateShape();
-            newShape.getLastProperty().setGeneric(this, value, oldShape, newShape);
+        }
+        Property property = newShape.getProperty(id);
+
+        if (oldShape == newShape) {
+            property.setSafe(this, value, oldShape);
         } else {
-            defineExisting(id, value, flags, existing, oldShape);
+            property.setSafe(this, value, oldShape, newShape);
         }
     }
 
--- a/truffle/com.oracle.truffle.object/src/com/oracle/truffle/object/LayoutImpl.java	Thu Oct 08 16:48:42 2015 -0400
+++ b/truffle/com.oracle.truffle.object/src/com/oracle/truffle/object/LayoutImpl.java	Thu Oct 08 17:00:53 2015 -0400
@@ -48,8 +48,8 @@
     private static final int INT_TO_DOUBLE_FLAG = 1;
     private static final int INT_TO_LONG_FLAG = 2;
 
-    private final LayoutStrategy strategy;
-    private final Class<? extends DynamicObject> clazz;
+    protected final LayoutStrategy strategy;
+    protected final Class<? extends DynamicObject> clazz;
     private final int allowedImplicitCasts;
 
     protected LayoutImpl(EnumSet<ImplicitCast> allowedImplicitCasts, Class<? extends DynamicObjectImpl> clazz, LayoutStrategy strategy) {
--- a/truffle/com.oracle.truffle.object/src/com/oracle/truffle/object/LayoutStrategy.java	Thu Oct 08 16:48:42 2015 -0400
+++ b/truffle/com.oracle.truffle.object/src/com/oracle/truffle/object/LayoutStrategy.java	Thu Oct 08 17:00:53 2015 -0400
@@ -23,26 +23,41 @@
 package com.oracle.truffle.object;
 
 import com.oracle.truffle.api.object.DynamicObject;
-import com.oracle.truffle.api.object.Layout;
 import com.oracle.truffle.api.object.Location;
 import com.oracle.truffle.api.object.Property;
 import com.oracle.truffle.api.object.Shape;
 import com.oracle.truffle.object.ShapeImpl.BaseAllocator;
 
-public interface LayoutStrategy {
-    boolean updateShape(DynamicObject object);
+public abstract class LayoutStrategy {
+    public abstract boolean updateShape(DynamicObject object);
+
+    public abstract ShapeImpl ensureValid(ShapeImpl newShape);
+
+    public abstract ShapeImpl ensureSpace(ShapeImpl shape, Location location);
 
-    Shape returnCached(Shape newShape);
+    public abstract boolean isAutoExtArray();
+
+    public abstract BaseAllocator createAllocator(LayoutImpl shape);
 
-    Shape ensureSpace(Shape shape, Location location);
+    public abstract BaseAllocator createAllocator(ShapeImpl shape);
 
-    boolean isAutoExtArray();
+    protected abstract ShapeAndProperty generalizeProperty(Property oldProperty, Object value, ShapeImpl currentShape, ShapeImpl nextShape);
 
-    Property generalizeProperty(DynamicObject object, Property oldProperty, Object value);
+    public static class ShapeAndProperty {
+        private final Shape shape;
+        private final Property property;
 
-    Property generalizeProperty(DynamicObject object, Property oldProperty, Object value, Shape oldShape, Shape newShape);
+        public ShapeAndProperty(Shape shape, Property property) {
+            this.shape = shape;
+            this.property = property;
+        }
 
-    BaseAllocator createAllocator(Layout shape);
+        public Shape getShape() {
+            return shape;
+        }
 
-    BaseAllocator createAllocator(Shape shape);
+        public Property getProperty() {
+            return property;
+        }
+    }
 }
--- a/truffle/com.oracle.truffle.object/src/com/oracle/truffle/object/ObjectStorageOptions.java	Thu Oct 08 16:48:42 2015 -0400
+++ b/truffle/com.oracle.truffle.object/src/com/oracle/truffle/object/ObjectStorageOptions.java	Thu Oct 08 17:00:53 2015 -0400
@@ -38,6 +38,7 @@
     public static boolean InObjectFields = booleanOption(OPTION_PREFIX + "InObjectFields", true);
 
     // Debug options (should be final)
+    public static final boolean DebugCounters = booleanOption(OPTION_PREFIX + "DebugCounters", true);
     public static final boolean TraceReshape = booleanOption(OPTION_PREFIX + "TraceReshape", false);
     public static final boolean DumpShapes = booleanOption(OPTION_PREFIX + "DumpShapes", false);
 
--- a/truffle/com.oracle.truffle.object/src/com/oracle/truffle/object/PropertyImpl.java	Thu Oct 08 16:48:42 2015 -0400
+++ b/truffle/com.oracle.truffle.object/src/com/oracle/truffle/object/PropertyImpl.java	Thu Oct 08 17:00:53 2015 -0400
@@ -22,17 +22,16 @@
  */
 package com.oracle.truffle.object;
 
+import java.util.Objects;
+
 import com.oracle.truffle.api.object.DynamicObject;
 import com.oracle.truffle.api.object.FinalLocationException;
 import com.oracle.truffle.api.object.HiddenKey;
 import com.oracle.truffle.api.object.IncompatibleLocationException;
 import com.oracle.truffle.api.object.Location;
-import com.oracle.truffle.api.object.LocationModifier;
 import com.oracle.truffle.api.object.Property;
 import com.oracle.truffle.api.object.Shape;
 import com.oracle.truffle.object.Locations.DeclaredLocation;
-import java.util.EnumSet;
-import java.util.Objects;
 
 /**
  * Property objects represent the mapping between property identifiers (keys) and storage locations.
@@ -213,27 +212,32 @@
     }
 
     private void setSlowCase(DynamicObject store, Object value) {
-        if (getLocation() instanceof DeclaredLocation) {
-            setDeclaredLocation(store, value);
-        } else {
-            generalize(store, value);
+        Shape oldShape = store.getShape();
+        Shape newShape = oldShape.defineProperty(getKey(), value, getFlags());
+        if (store.updateShape()) {
+            oldShape = store.getShape();
         }
+        assert newShape.isValid() && oldShape.isValid();
+        Property newProperty = newShape.getProperty(getKey());
+        newProperty.setSafe(store, value, oldShape, newShape);
     }
 
-    private void setDeclaredLocation(DynamicObject store, Object value) {
-        store.updateShape();
-        Shape oldShape = store.getShape();
-        Shape newShape = oldShape.addProperty(this.relocateShadow(oldShape.allocator().locationForValue(value, EnumSet.of(LocationModifier.Final, LocationModifier.NonNull))));
-        store.updateShape();
-        newShape.getLastProperty().setGeneric(store, value, oldShape, newShape);
-    }
+    private void setWithShapeSlowCase(DynamicObject store, Object value, Shape currentShape, Shape nextShape) {
+        Shape oldShape = currentShape;
+        if (store.updateShape()) {
+            oldShape = store.getShape();
+        }
+        LayoutStrategy strategy = ((LayoutImpl) currentShape.getLayout()).getStrategy();
+        LayoutStrategy.ShapeAndProperty newShapeAndProperty = strategy.generalizeProperty(this, value, (ShapeImpl) oldShape, (ShapeImpl) nextShape);
+        if (store.updateShape()) {
+            oldShape = store.getShape();
+        }
 
-    private Property generalize(DynamicObject store, Object value) {
-        return ((LayoutImpl) store.getShape().getLayout()).getStrategy().generalizeProperty(store, this, value);
-    }
+        Shape newNextShape = newShapeAndProperty.getShape();
+        Property newProperty = newShapeAndProperty.getProperty();
 
-    private void setWithShapeSlowCase(DynamicObject store, Object value, Shape oldShape, Shape newShape) {
-        ((LayoutImpl) store.getShape().getLayout()).getStrategy().generalizeProperty(store, this, value, oldShape, newShape);
+        assert newNextShape.isValid() && oldShape.isValid();
+        newProperty.setSafe(store, value, oldShape, newNextShape);
     }
 
     @Override
@@ -246,7 +250,7 @@
         return shadow;
     }
 
-    private Property relocateShadow(Location newLocation) {
+    Property relocateShadow(Location newLocation) {
         assert !isShadow() && getLocation() instanceof DeclaredLocation && relocatable;
         return new PropertyImpl(key, newLocation, flags, true, relocatable);
     }
--- a/truffle/com.oracle.truffle.object/src/com/oracle/truffle/object/ShapeImpl.java	Thu Oct 08 16:48:42 2015 -0400
+++ b/truffle/com.oracle.truffle.object/src/com/oracle/truffle/object/ShapeImpl.java	Thu Oct 08 17:00:53 2015 -0400
@@ -22,6 +22,16 @@
  */
 package com.oracle.truffle.object;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+
 import com.oracle.truffle.api.Assumption;
 import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives;
@@ -34,6 +44,7 @@
 import com.oracle.truffle.api.object.DynamicObjectFactory;
 import com.oracle.truffle.api.object.Layout;
 import com.oracle.truffle.api.object.Location;
+import com.oracle.truffle.api.object.LocationFactory;
 import com.oracle.truffle.api.object.ObjectLocation;
 import com.oracle.truffle.api.object.ObjectType;
 import com.oracle.truffle.api.object.Property;
@@ -53,15 +64,6 @@
 import com.oracle.truffle.object.Transition.PropertyTransition;
 import com.oracle.truffle.object.Transition.RemovePropertyTransition;
 import com.oracle.truffle.object.Transition.ReservePrimitiveArrayTransition;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * Shape objects create a mapping of Property objects to indexes. The mapping of those indexes to an
@@ -312,7 +314,7 @@
         ShapeImpl cachedShape = this.getTransitionMapForRead().get(transition);
         if (cachedShape != null) { // Shape already exists?
             shapeCacheHitCount.inc();
-            return (ShapeImpl) layout.getStrategy().returnCached(cachedShape);
+            return layout.getStrategy().ensureValid(cachedShape);
         }
         shapeCacheMissCount.inc();
 
@@ -339,6 +341,40 @@
         }
     }
 
+    @TruffleBoundary
+    @Override
+    public ShapeImpl defineProperty(Object key, Object value, int flags) {
+        return defineProperty(key, value, flags, DEFAULT_LAYOUT_FACTORY);
+    }
+
+    @TruffleBoundary
+    @Override
+    public ShapeImpl defineProperty(Object key, Object value, int flags, LocationFactory locationFactory) {
+        ShapeImpl oldShape = this;
+        if (!oldShape.isValid()) {
+            oldShape = layout.getStrategy().ensureValid(oldShape);
+        }
+        PropertyImpl existing = (PropertyImpl) oldShape.getProperty(key);
+        if (existing == null) {
+            return oldShape.addProperty(Property.create(key, locationFactory.createLocation(oldShape, value), flags));
+        } else {
+            if (existing.getFlags() == flags) {
+                if (existing.getLocation().canSet(value)) {
+                    return oldShape;
+                } else {
+                    if (existing.getLocation() instanceof DeclaredLocation) {
+                        return oldShape.addProperty(existing.relocateShadow(locationFactory.createLocation(oldShape, value)));
+                    } else {
+                        return (ShapeImpl) layout.getStrategy().generalizeProperty(existing, value, oldShape, oldShape).getShape();
+                    }
+                }
+            } else {
+                Property newProperty = Property.create(key, oldShape.getLayout().existingLocationForValue(value, existing.getLocation(), oldShape), flags);
+                return oldShape.replaceProperty(existing, newProperty);
+            }
+        }
+    }
+
     /**
      * Add a new property in the map, yielding a new or cached Shape object.
      *
@@ -357,7 +393,7 @@
             return cachedShape;
         }
 
-        ShapeImpl oldShape = (ShapeImpl) layout.getStrategy().ensureSpace(this, prop.getLocation());
+        ShapeImpl oldShape = layout.getStrategy().ensureSpace(this, prop.getLocation());
 
         ShapeImpl newShape = makeShapeWithAddedProperty(oldShape, addTransition);
         oldShape.addDirectTransition(addTransition, newShape);
@@ -424,7 +460,7 @@
             return cachedShape;
         }
 
-        ShapeImpl oldShape = (ShapeImpl) layout.getStrategy().ensureSpace(this, layout.getPrimitiveArrayLocation());
+        ShapeImpl oldShape = layout.getStrategy().ensureSpace(this, layout.getPrimitiveArrayLocation());
         ShapeImpl newShape = makeShapeWithPrimitiveExtensionArray(oldShape, transition);
         oldShape.addDirectTransition(transition, newShape);
         return newShape;
@@ -1105,6 +1141,12 @@
         }
     };
 
+    static final LocationFactory DEFAULT_LAYOUT_FACTORY = new LocationFactory() {
+        public Location createLocation(Shape shape, Object value) {
+            return ((ShapeImpl) shape).allocator().locationForValue(value, true, value != null);
+        }
+    };
+
     private static final DebugCounter shapeCount = DebugCounter.create("Shapes allocated total");
     private static final DebugCounter shapeCloneCount = DebugCounter.create("Shapes allocated cloned");
     private static final DebugCounter shapeCacheHitCount = DebugCounter.create("Shape cache hits");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLDebugTest.java	Thu Oct 08 17:00:53 2015 -0400
@@ -0,0 +1,241 @@
+/*
+ * 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.debug.Debugger;
+import com.oracle.truffle.api.debug.ExecutionEvent;
+import com.oracle.truffle.api.debug.SuspendedEvent;
+import com.oracle.truffle.api.frame.MaterializedFrame;
+import com.oracle.truffle.api.source.LineLocation;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.api.vm.EventConsumer;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+import java.io.ByteArrayOutputStream;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import org.junit.Test;
+
+public class SLDebugTest {
+    private Source factorial;
+    private Debugger debugger;
+    private Callable<?> run;
+    private SuspendedEvent suspendedEvent;
+    private Throwable ex;
+    private ExecutionEvent executionEvent;
+
+    @Test
+    public void stepInStepOver() throws Throwable {
+        // @formatter:off
+        factorial = Source.fromText(
+            "function main() {\n" +
+            "  res = fac(2);\n" +
+            "  println(res);\n" +
+            "  return res;\n" +
+            "}\n" +
+            "function fac(n) {\n" +
+            "  if (n <= 1) {\n" +
+            "    return 1;\n" +
+            "  }\n" +
+            "  nMinusOne = n - 1;\n" +
+            "  nMOFact = fac(nMinusOne);\n" +
+            "  res = n * nMOFact;\n" +
+            "  return res;\n" +
+            "}\n" +
+             "", "factorial.sl"
+        ).withMimeType("application/x-sl");
+        // @formatter:on
+
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+        PolyglotEngine engine = PolyglotEngine.buildNew().executor(Executors.newSingleThreadExecutor()).onEvent(new EventConsumer<ExecutionEvent>(ExecutionEvent.class) {
+            @Override
+            protected void on(ExecutionEvent event) {
+                onExecution(event);
+            }
+        }).onEvent(new EventConsumer<SuspendedEvent>(SuspendedEvent.class) {
+            @Override
+            protected void on(SuspendedEvent event) {
+                onSuspended(event);
+            }
+        }).setOut(os).build();
+
+        PolyglotEngine.Value value;
+        synchronized (this) {
+            value = engine.eval(factorial);
+            wait();
+        }
+        assertNotNull("Debugger initalized", debugger);
+
+        run(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                LineLocation nMinusOne = factorial.createLineLocation(7);
+                debugger.setLineBreakpoint(0, nMinusOne, true);
+                executionEvent.prepareContinue();
+                return null;
+            }
+        });
+
+        assertNull("Parsing done", value.get());
+
+        PolyglotEngine.Value main = engine.findGlobalSymbol("main");
+        value = main.invoke(null);
+
+        run(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                assertNull(suspendedEvent);
+                executionEvent.prepareStepInto();
+                return null;
+            }
+        });
+
+        run(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                assertNotNull(suspendedEvent);
+                final MaterializedFrame frame = suspendedEvent.getFrame();
+                assertEquals("No arguments", 0, frame.getArguments().length);
+                assertEquals("one var slot", 1, frame.getFrameDescriptor().getSlots().size());
+                Object resName = frame.getFrameDescriptor().getSlots().get(0).getFrameDescriptor().getIdentifiers().iterator().next();
+                assertEquals("res", resName);
+                suspendedEvent.prepareStepInto(2);
+                suspendedEvent = null;
+                return null;
+            }
+        });
+
+        run(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                assertLine(6);
+
+                final MaterializedFrame frame = suspendedEvent.getFrame();
+                assertEquals("One argument", 1, frame.getArguments().length);
+                assertEquals("One argument value 2", 2L, frame.getArguments()[0]);
+                suspendedEvent.prepareStepOver(1);
+                suspendedEvent = null;
+                return null;
+            }
+        });
+
+        run(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                assertNotNull(suspendedEvent);
+
+                // XXX wrong step over:
+                // assertLine(7);
+
+                suspendedEvent.prepareContinue();
+                suspendedEvent = null;
+                return null;
+            }
+        });
+
+        run(null);
+
+        Number n = value.as(Number.class);
+        assertNotNull(n);
+        assertEquals("Factorial computed OK", 2, n.intValue());
+    }
+
+    synchronized void onExecution(ExecutionEvent event) {
+        executionEvent = event;
+        debugger = event.getDebugger();
+        notifyAll();
+        waitForWork();
+    }
+
+    synchronized void onSuspended(SuspendedEvent event) {
+        suspendedEvent = event;
+        notifyAll();
+        waitForWork();
+    }
+
+    private synchronized void run(Callable<?> callable) throws Throwable {
+        if (ex != null) {
+            throw ex;
+        }
+        while (run != null) {
+            wait();
+        }
+        run = callable;
+        notifyAll();
+        if (ex != null) {
+            throw ex;
+        }
+    }
+
+    private void waitForWork() {
+        try {
+            waitForWork0().call();
+        } catch (Throwable tmpEx) {
+            this.ex = tmpEx;
+        }
+
+    }
+
+    private synchronized Callable<?> waitForWork0() {
+        while (run == null) {
+            try {
+                wait();
+            } catch (InterruptedException tmpEx) {
+                throw new IllegalStateException(tmpEx);
+            }
+        }
+        Callable<?> c = run;
+        run = null;
+        notifyAll();
+        return c;
+    }
+
+    void assertLine(int line) {
+        assertNotNull(suspendedEvent);
+        final SourceSection expLoc = factorial.createSection("Line " + line, line);
+        final SourceSection sourceLoc = suspendedEvent.getNode().getEncapsulatingSourceSection();
+        assertEquals("Exp\n" + expLoc + "\nbut was\n" + sourceLoc, line, sourceLoc.getLineLocation().getLineNumber());
+    }
+}
--- a/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTckTest.java	Thu Oct 08 16:48:42 2015 -0400
+++ b/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTckTest.java	Thu Oct 08 17:00:53 2015 -0400
@@ -165,6 +165,11 @@
         return "count";
     }
 
+    @Override
+    protected String globalObject() {
+        return null;
+    }
+
     //
     // Ignore tests working on floats and double
     //
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/ToStringOfEvalTest.java	Thu Oct 08 17:00:53 2015 -0400
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2012, 2015, 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.source.Source;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+import java.io.IOException;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+
+public class ToStringOfEvalTest {
+
+    @Test
+    public void checkToStringOnAFunction() throws IOException {
+        PolyglotEngine engine = PolyglotEngine.buildNew().build();
+        PolyglotEngine.Language sl = engine.getLanguages().get("application/x-sl");
+        sl.eval(Source.fromText("function checkName() {}", "defineFn"));
+        PolyglotEngine.Value value1 = engine.findGlobalSymbol("checkName");
+        PolyglotEngine.Value value2 = engine.findGlobalSymbol("checkName");
+
+        assertNotNull("Symbol is not null", value1);
+        assertNotNull("Symbol is not null either", value2);
+
+        Object global1 = value1.get();
+        Object global2 = value2.get();
+
+        assertNotNull("Symbol is not null", global1);
+        assertNotNull("Symbol is not null either", global2);
+
+        assertEquals("Symbols are the same", global1, global2);
+
+        assertTrue("Contans checkName text: " + global2, global2.toString().contains("checkName"));
+    }
+}
--- a/truffle/com.oracle.truffle.sl.tools/src/com/oracle/truffle/sl/tools/debug/SLREPLServer.java	Thu Oct 08 16:48:42 2015 -0400
+++ b/truffle/com.oracle.truffle.sl.tools/src/com/oracle/truffle/sl/tools/debug/SLREPLServer.java	Thu Oct 08 17:00:53 2015 -0400
@@ -78,7 +78,7 @@
     public static void main(String[] args) {
         // Cheating for the prototype: start from SL, rather than from the client.
         final SLREPLServer server = new SLREPLServer();
-        final SimpleREPLClient client = new SimpleREPLClient(server.language.getShortName(), server);
+        final SimpleREPLClient client = new SimpleREPLClient(getShortName(server.language), server);
 
         // Cheating for the prototype: allow server access to client for recursive debugging
         server.setClient(client);
@@ -89,6 +89,10 @@
         }
     }
 
+    private static String getShortName(Language language) {
+        return language.getName() + "(" + language.getVersion() + ")";
+    }
+
     private final Language language;
     private final PolyglotEngine vm;
     private Debugger db;
@@ -145,7 +149,7 @@
         assert language != null;
 
         this.vm = newVM;
-        this.statusPrefix = language.getShortName() + " REPL:";
+        this.statusPrefix = getShortName(language) + " REPL:";
     }
 
     private void setClient(SimpleREPLClient replClient) {
@@ -160,7 +164,7 @@
         // SL doesn't load modules (like other languages), so we just return a success
         final REPLMessage reply = new REPLMessage();
         reply.put(REPLMessage.STATUS, REPLMessage.SUCCEEDED);
-        reply.put(REPLMessage.DISPLAY_MSG, language.getShortName() + " started");
+        reply.put(REPLMessage.DISPLAY_MSG, getShortName(language) + " started");
         return reply;
     }
 
--- a/truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/TruffleTCK.java	Thu Oct 08 16:48:42 2015 -0400
+++ b/truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/TruffleTCK.java	Thu Oct 08 17:00:53 2015 -0400
@@ -24,10 +24,12 @@
  */
 package com.oracle.truffle.tck;
 
+import com.oracle.truffle.api.TruffleLanguage;
 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 com.oracle.truffle.api.vm.PolyglotEngine.Language;
 import java.io.IOException;
 import java.lang.reflect.Field;
 import java.util.Random;
@@ -142,6 +144,19 @@
     }
 
     /**
+     * Name of a function to return global object. The function can be executed without providing
+     * any arguments and should return global object of the language, if the language supports it.
+     * Global object is the one accessible via
+     * {@link TruffleLanguage#getLanguageGlobal(java.lang.Object)}.
+     *
+     * @return name of globally exported symbol, return <code>null</code> if the language doesn't
+     *         support the concept of global object
+     */
+    protected String globalObject() {
+        throw new UnsupportedOperationException("globalObject() method not implemented");
+    }
+
+    /**
      * Name of a function that counts number of its invocations in current {@link PolyglotEngine}
      * context. Your function should somehow keep a counter to remember number of its invocations
      * and always increment it. The first invocation should return <code>1</code>, the second
@@ -232,6 +247,15 @@
     }
 
     @Test
+    public void testNullCanBeCastToAnything() throws Exception {
+        PolyglotEngine.Value retNull = findGlobalSymbol(returnsNull());
+
+        Object res = retNull.invoke(null).as(CompoundObject.class);
+
+        assertNull("Should yield real Java null", res);
+    }
+
+    @Test
     public void testNullInCompoundObject() throws Exception {
         CompoundObject obj = findCompoundSymbol();
         if (obj == null) {
@@ -351,8 +375,8 @@
         final PolyglotEngine.Value result = apply.invoke(null, fn);
 
         try {
-            String res = result.as(String.class);
-            fail("Cannot be converted to String: " + res);
+            Boolean res = result.as(Boolean.class);
+            fail("Cannot be converted to Boolean: " + res);
         } catch (ClassCastException ex) {
             // correct
         }
@@ -553,7 +577,21 @@
             }
             assert prev1 == prev2 : "At round " + i + " the same number of invocations " + prev1 + " vs. " + prev2;
         }
+    }
 
+    @Test
+    public void testGlobalObjectIsAccessible() throws Exception {
+        String globalObjectFunction = globalObject();
+        if (globalObjectFunction == null) {
+            return;
+        }
+
+        Language language = vm().getLanguages().get(mimeType());
+        assertNotNull("Langugage for " + mimeType() + " found", language);
+
+        PolyglotEngine.Value function = vm().findGlobalSymbol(globalObjectFunction);
+        Object global = function.invoke(null).get();
+        assertEquals("Global from the language same with Java obtained one", language.getGlobalObject().get(), global);
     }
 
     private PolyglotEngine.Value findGlobalSymbol(String name) throws Exception {