changeset 18412:997bc9764a9a

SL: use the truffle object storage model to represent SL objects
author Andreas Woess <andreas.woess@jku.at>
date Tue, 18 Nov 2014 12:08:51 +0100
parents dc2e000bed40
children 12cae938aade
files graal/com.oracle.truffle.sl.test/tests/Object.sl graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLNewObjectBuiltin.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/access/SLReadPropertyCacheNode.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/access/SLReadPropertyNode.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/access/SLWritePropertyCacheNode.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/access/SLWritePropertyNode.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java mx/suite.py
diffstat 8 files changed, 542 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.truffle.sl.test/tests/Object.sl	Tue Nov 18 23:02:58 2014 +0100
+++ b/graal/com.oracle.truffle.sl.test/tests/Object.sl	Tue Nov 18 12:08:51 2014 +0100
@@ -15,6 +15,14 @@
   obj3 = new();
   obj3.fn = mkobj;
   println(obj3.fn().z);
+
+  obj4 = new();
+  write(obj4, 1);
+  read(obj4);
+  write(obj4, 2);
+  read(obj4);
+  write(obj4, "three");
+  read(obj4);
 }
 
 function mkobj() {
@@ -22,3 +30,11 @@
   newobj.z = "zzz";
   return newobj;
 }
+
+function read(obj) {
+  return obj.prop;
+}
+
+function write(obj, value) {
+  return obj.prop = value;
+}
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLNewObjectBuiltin.java	Tue Nov 18 23:02:58 2014 +0100
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLNewObjectBuiltin.java	Tue Nov 18 12:08:51 2014 +0100
@@ -22,8 +22,6 @@
  */
 package com.oracle.truffle.sl.builtins;
 
-import java.util.*;
-
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.nodes.*;
@@ -48,6 +46,6 @@
 
     @Specialization
     public Object newObject() {
-        return new HashMap<>();
+        return getContext().createObject();
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/access/SLReadPropertyCacheNode.java	Tue Nov 18 12:08:51 2014 +0100
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2013, 2014, 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.sl.nodes.access;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.object.*;
+import com.oracle.truffle.sl.nodes.*;
+import com.oracle.truffle.sl.runtime.*;
+
+/**
+ * The node for accessing a property of an object. When executed, this node first evaluates the
+ * object expression on the left side of the dot operator and then reads the named property.
+ */
+public abstract class SLReadPropertyCacheNode extends Node {
+
+    public static SLReadPropertyCacheNode create(String propertyName) {
+        return new SLUninitializedReadObjectPropertyNode(propertyName);
+    }
+
+    public abstract Object executeObject(DynamicObject receiver);
+
+    public abstract long executeLong(DynamicObject receiver) throws UnexpectedResultException;
+
+    protected abstract static class SLReadPropertyCacheChainNode extends SLReadPropertyCacheNode {
+        protected final Shape shape;
+        @Child protected SLReadPropertyCacheNode next;
+
+        public SLReadPropertyCacheChainNode(Shape shape, SLReadPropertyCacheNode next) {
+            this.shape = shape;
+            this.next = next;
+        }
+
+        @Override
+        public final Object executeObject(DynamicObject receiver) {
+            try {
+                // if this assumption fails, the object needs to be updated to a valid shape
+                shape.getValidAssumption().check();
+            } catch (InvalidAssumptionException e) {
+                return this.replace(next).executeObject(receiver);
+            }
+
+            boolean condition = shape.check(receiver);
+
+            if (condition) {
+                return executeObjectUnchecked(receiver, condition);
+            } else {
+                return next.executeObject(receiver);
+            }
+        }
+
+        @Override
+        public final long executeLong(DynamicObject receiver) throws UnexpectedResultException {
+            try {
+                // if this assumption fails, the object needs to be updated to a valid shape
+                shape.getValidAssumption().check();
+            } catch (InvalidAssumptionException e) {
+                return this.replace(next).executeLong(receiver);
+            }
+
+            boolean condition = shape.check(receiver);
+
+            if (condition) {
+                return executeLongUnchecked(receiver, condition);
+            } else {
+                return next.executeLong(receiver);
+            }
+        }
+
+        protected abstract Object executeObjectUnchecked(DynamicObject receiver, boolean condition);
+
+        protected long executeLongUnchecked(DynamicObject receiver, boolean condition) throws UnexpectedResultException {
+            return SLTypesGen.SLTYPES.expectLong(executeObjectUnchecked(receiver, condition));
+        }
+    }
+
+    protected static class SLReadObjectPropertyNode extends SLReadPropertyCacheChainNode {
+        private final Location location;
+
+        protected SLReadObjectPropertyNode(Shape shape, Location location, SLReadPropertyCacheNode next) {
+            super(shape, next);
+            this.location = location;
+        }
+
+        @Override
+        protected Object executeObjectUnchecked(DynamicObject receiver, boolean condition) {
+            return location.get(receiver, condition);
+        }
+    }
+
+    protected static class SLReadBooleanPropertyNode extends SLReadPropertyCacheChainNode {
+        private final BooleanLocation location;
+
+        protected SLReadBooleanPropertyNode(Shape shape, BooleanLocation location, SLReadPropertyCacheNode next) {
+            super(shape, next);
+            this.location = location;
+        }
+
+        @Override
+        protected Object executeObjectUnchecked(DynamicObject receiver, boolean condition) {
+            return location.getBoolean(receiver, condition);
+        }
+    }
+
+    protected static class SLReadLongPropertyNode extends SLReadPropertyCacheChainNode {
+        private final LongLocation location;
+
+        protected SLReadLongPropertyNode(Shape shape, LongLocation location, SLReadPropertyCacheNode next) {
+            super(shape, next);
+            this.location = location;
+        }
+
+        @Override
+        protected Object executeObjectUnchecked(DynamicObject receiver, boolean condition) {
+            return location.getLong(receiver, condition);
+        }
+
+        @Override
+        protected long executeLongUnchecked(DynamicObject receiver, boolean condition) throws UnexpectedResultException {
+            return location.getLong(receiver, condition);
+        }
+    }
+
+    protected static class SLReadMissingPropertyNode extends SLReadPropertyCacheChainNode {
+        protected SLReadMissingPropertyNode(Shape shape, SLReadPropertyCacheNode next) {
+            super(shape, next);
+        }
+
+        @Override
+        protected Object executeObjectUnchecked(DynamicObject receiver, boolean condition) {
+            // The property was not found in the object, return null
+            return SLNull.SINGLETON;
+        }
+    }
+
+    protected static class SLUninitializedReadObjectPropertyNode extends SLReadPropertyCacheNode {
+        protected final String propertyName;
+
+        protected SLUninitializedReadObjectPropertyNode(String propertyName) {
+            this.propertyName = propertyName;
+        }
+
+        @Override
+        public Object executeObject(DynamicObject receiver) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+
+            receiver.updateShape();
+
+            Shape shape = receiver.getShape();
+            Property property = shape.getProperty(propertyName);
+
+            final SLReadPropertyCacheNode resolvedNode;
+            if (property == null) {
+                resolvedNode = new SLReadMissingPropertyNode(shape, this);
+            } else if (property.getLocation() instanceof LongLocation) {
+                resolvedNode = new SLReadLongPropertyNode(shape, (LongLocation) property.getLocation(), this);
+            } else if (property.getLocation() instanceof BooleanLocation) {
+                resolvedNode = new SLReadBooleanPropertyNode(shape, (BooleanLocation) property.getLocation(), this);
+            } else {
+                resolvedNode = new SLReadObjectPropertyNode(shape, property.getLocation(), this);
+            }
+
+            return this.replace(resolvedNode, "resolved '" + propertyName + "'").executeObject(receiver);
+        }
+
+        @Override
+        public long executeLong(DynamicObject receiver) throws UnexpectedResultException {
+            return SLTypesGen.SLTYPES.expectLong(executeObject(receiver));
+        }
+    }
+}
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/access/SLReadPropertyNode.java	Tue Nov 18 23:02:58 2014 +0100
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/access/SLReadPropertyNode.java	Tue Nov 18 12:08:51 2014 +0100
@@ -22,13 +22,13 @@
  */
 package com.oracle.truffle.sl.nodes.access;
 
-import java.util.*;
-
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.api.source.*;
+import com.oracle.truffle.api.utilities.*;
 import com.oracle.truffle.sl.*;
 import com.oracle.truffle.sl.nodes.*;
+import com.oracle.truffle.sl.runtime.*;
 
 /**
  * The node for accessing a property of an object. When executed, this node first evaluates the
@@ -43,18 +43,21 @@
 
     @Child protected SLExpressionNode receiverNode;
     protected final String propertyName;
+    @Child protected SLReadPropertyCacheNode cacheNode;
+    private final ConditionProfile receiverTypeCondition = ConditionProfile.createBinaryProfile();
 
     private SLReadPropertyNode(SourceSection src, SLExpressionNode receiverNode, String propertyName) {
         super(src);
         this.receiverNode = receiverNode;
         this.propertyName = propertyName;
+        this.cacheNode = SLReadPropertyCacheNode.create(propertyName);
     }
 
     @Override
     public Object executeGeneric(VirtualFrame frame) {
         Object object = receiverNode.executeGeneric(frame);
-        if (object instanceof Map) {
-            return ((Map<?, ?>) object).get(propertyName);
+        if (receiverTypeCondition.profile(SLContext.isSLObject(object))) {
+            return cacheNode.executeObject(SLContext.castSLObject(object));
         } else {
             throw new SLException("unexpected receiver type");
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/access/SLWritePropertyCacheNode.java	Tue Nov 18 12:08:51 2014 +0100
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 2013, 2014, 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.sl.nodes.access;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.object.*;
+
+/**
+ * The node for accessing a property of an object. When executed, this node first evaluates the
+ * object expression on the left side of the dot operator and then reads the named property.
+ */
+public abstract class SLWritePropertyCacheNode extends Node {
+
+    public static SLWritePropertyCacheNode create(String propertyName) {
+        return new SLUninitializedWritePropertyNode(propertyName);
+    }
+
+    public abstract void executeObject(DynamicObject receiver, Object value);
+
+    public abstract void executeLong(DynamicObject receiver, long value);
+
+    public abstract void executeBoolean(DynamicObject receiver, boolean value);
+
+    protected abstract static class SLWritePropertyCacheChainNode extends SLWritePropertyCacheNode {
+        protected final Shape oldShape;
+        protected final Shape newShape;
+        @Child protected SLWritePropertyCacheNode next;
+
+        public SLWritePropertyCacheChainNode(Shape oldShape, Shape newShape, SLWritePropertyCacheNode next) {
+            this.oldShape = oldShape;
+            this.newShape = newShape;
+            this.next = next;
+        }
+
+        @Override
+        public final void executeObject(DynamicObject receiver, Object value) {
+            try {
+                // if this assumption fails, the object needs to be updated to a valid shape
+                oldShape.getValidAssumption().check();
+                newShape.getValidAssumption().check();
+            } catch (InvalidAssumptionException e) {
+                this.replace(next).executeObject(receiver, value);
+                return;
+            }
+
+            boolean condition = oldShape.check(receiver) && checkValue(receiver, value);
+
+            if (condition) {
+                executeObjectUnchecked(receiver, value);
+            } else {
+                next.executeObject(receiver, value);
+            }
+        }
+
+        @Override
+        public final void executeLong(DynamicObject receiver, long value) {
+            try {
+                // if this assumption fails, the object needs to be updated to a valid shape
+                oldShape.getValidAssumption().check();
+                newShape.getValidAssumption().check();
+            } catch (InvalidAssumptionException e) {
+                this.replace(next).executeLong(receiver, value);
+                return;
+            }
+
+            boolean condition = oldShape.check(receiver) && checkValue(receiver, value);
+
+            if (condition) {
+                executeLongUnchecked(receiver, value);
+            } else {
+                next.executeLong(receiver, value);
+            }
+        }
+
+        @Override
+        public final void executeBoolean(DynamicObject receiver, boolean value) {
+            try {
+                // if this assumption fails, the object needs to be updated to a valid shape
+                oldShape.getValidAssumption().check();
+                newShape.getValidAssumption().check();
+            } catch (InvalidAssumptionException e) {
+                this.replace(next).executeBoolean(receiver, value);
+                return;
+            }
+
+            boolean condition = oldShape.check(receiver) && checkValue(receiver, value);
+
+            if (condition) {
+                executeBooleanUnchecked(receiver, value);
+            } else {
+                next.executeBoolean(receiver, value);
+            }
+        }
+
+        @SuppressWarnings("unused")
+        protected boolean checkValue(DynamicObject receiver, Object value) {
+            return true;
+        }
+
+        protected abstract void executeObjectUnchecked(DynamicObject receiver, Object value);
+
+        protected void executeLongUnchecked(DynamicObject receiver, long value) {
+            executeObjectUnchecked(receiver, value);
+        }
+
+        protected void executeBooleanUnchecked(DynamicObject receiver, boolean value) {
+            executeObjectUnchecked(receiver, value);
+        }
+    }
+
+    protected static class SLWriteObjectPropertyNode extends SLWritePropertyCacheChainNode {
+        private final Location location;
+
+        protected SLWriteObjectPropertyNode(Shape oldShape, Shape newShape, Location location, SLWritePropertyCacheNode next) {
+            super(oldShape, newShape, next);
+            this.location = location;
+        }
+
+        @Override
+        protected void executeObjectUnchecked(DynamicObject receiver, Object value) {
+            try {
+                if (oldShape == newShape) {
+                    location.set(receiver, value, oldShape);
+                } else {
+                    location.set(receiver, value, oldShape, newShape);
+                }
+            } catch (IncompatibleLocationException | FinalLocationException e) {
+                replace(next).executeObject(receiver, value);
+            }
+        }
+
+        @Override
+        protected boolean checkValue(DynamicObject receiver, Object value) {
+            return location.canSet(receiver, value);
+        }
+    }
+
+    protected static class SLWriteBooleanPropertyNode extends SLWritePropertyCacheChainNode {
+        private final BooleanLocation location;
+
+        protected SLWriteBooleanPropertyNode(Shape oldShape, Shape newShape, BooleanLocation location, SLWritePropertyCacheNode next) {
+            super(oldShape, newShape, next);
+            this.location = location;
+        }
+
+        @Override
+        protected void executeObjectUnchecked(DynamicObject receiver, Object value) {
+            try {
+                if (oldShape == newShape) {
+                    location.set(receiver, value, oldShape);
+                } else {
+                    location.set(receiver, value, oldShape, newShape);
+                }
+            } catch (IncompatibleLocationException | FinalLocationException e) {
+                replace(next).executeObject(receiver, value);
+            }
+        }
+
+        @Override
+        protected void executeBooleanUnchecked(DynamicObject receiver, boolean value) {
+            try {
+                if (oldShape == newShape) {
+                    location.setBoolean(receiver, value, oldShape);
+                } else {
+                    location.setBoolean(receiver, value, oldShape, newShape);
+                }
+            } catch (FinalLocationException e) {
+                replace(next).executeBoolean(receiver, value);
+            }
+        }
+
+        @Override
+        protected boolean checkValue(DynamicObject receiver, Object value) {
+            return value instanceof Boolean;
+        }
+    }
+
+    protected static class SLWriteLongPropertyNode extends SLWritePropertyCacheChainNode {
+        private final LongLocation location;
+
+        protected SLWriteLongPropertyNode(Shape oldShape, Shape newShape, LongLocation location, SLWritePropertyCacheNode next) {
+            super(oldShape, newShape, next);
+            this.location = location;
+        }
+
+        @Override
+        protected void executeObjectUnchecked(DynamicObject receiver, Object value) {
+            try {
+                if (oldShape == newShape) {
+                    location.set(receiver, value, oldShape);
+                } else {
+                    location.set(receiver, value, oldShape, newShape);
+                }
+            } catch (IncompatibleLocationException | FinalLocationException e) {
+                replace(next).executeObject(receiver, value);
+            }
+        }
+
+        @Override
+        protected void executeLongUnchecked(DynamicObject receiver, long value) {
+            try {
+                if (oldShape == newShape) {
+                    location.setLong(receiver, value, oldShape);
+                } else {
+                    location.setLong(receiver, value, oldShape, newShape);
+                }
+            } catch (FinalLocationException e) {
+                replace(next).executeLong(receiver, value);
+            }
+        }
+
+        @Override
+        protected boolean checkValue(DynamicObject receiver, Object value) {
+            return value instanceof Long;
+        }
+    }
+
+    protected static class SLUninitializedWritePropertyNode extends SLWritePropertyCacheNode {
+        protected final String propertyName;
+
+        protected SLUninitializedWritePropertyNode(String propertyName) {
+            this.propertyName = propertyName;
+        }
+
+        @Override
+        public void executeObject(DynamicObject receiver, Object value) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+
+            if (receiver.updateShape()) {
+                // shape changed, retry cache again
+                getTopNode().executeObject(receiver, value);
+                return;
+            }
+
+            Shape oldShape = receiver.getShape();
+            Shape newShape;
+            Property property = oldShape.getProperty(propertyName);
+
+            final SLWritePropertyCacheNode resolvedNode;
+            if (property != null && property.getLocation().canSet(receiver, value)) {
+                newShape = oldShape;
+            } else {
+                receiver.define(propertyName, value, 0);
+                newShape = receiver.getShape();
+                property = newShape.getProperty(propertyName);
+            }
+
+            if (property.getLocation() instanceof LongLocation) {
+                resolvedNode = new SLWriteLongPropertyNode(oldShape, newShape, (LongLocation) property.getLocation(), this);
+            } else if (property.getLocation() instanceof BooleanLocation) {
+                resolvedNode = new SLWriteBooleanPropertyNode(oldShape, newShape, (BooleanLocation) property.getLocation(), this);
+            } else {
+                resolvedNode = new SLWriteObjectPropertyNode(oldShape, newShape, property.getLocation(), this);
+            }
+
+            this.replace(resolvedNode, "resolved '" + propertyName + "'").executeObject(receiver, value);
+        }
+
+        private SLWritePropertyCacheNode getTopNode() {
+            SLWritePropertyCacheNode top = this;
+            while (top.getParent() instanceof SLWritePropertyCacheNode) {
+                top = (SLWritePropertyCacheNode) top.getParent();
+            }
+            return top;
+        }
+
+        @Override
+        public void executeLong(DynamicObject receiver, long value) {
+            executeObject(receiver, value);
+        }
+
+        @Override
+        public void executeBoolean(DynamicObject receiver, boolean value) {
+            executeObject(receiver, value);
+        }
+    }
+}
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/access/SLWritePropertyNode.java	Tue Nov 18 23:02:58 2014 +0100
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/access/SLWritePropertyNode.java	Tue Nov 18 12:08:51 2014 +0100
@@ -22,13 +22,13 @@
  */
 package com.oracle.truffle.sl.nodes.access;
 
-import java.util.*;
-
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.api.source.*;
+import com.oracle.truffle.api.utilities.*;
 import com.oracle.truffle.sl.*;
 import com.oracle.truffle.sl.nodes.*;
+import com.oracle.truffle.sl.runtime.*;
 
 /**
  * The node for setting a property of an object. When executed, this node first evaluates the value
@@ -46,21 +46,23 @@
     @Child protected SLExpressionNode receiverNode;
     protected final String propertyName;
     @Child protected SLExpressionNode valueNode;
+    @Child protected SLWritePropertyCacheNode cacheNode;
+    private final ConditionProfile receiverTypeCondition = ConditionProfile.createBinaryProfile();
 
     private SLWritePropertyNode(SourceSection src, SLExpressionNode receiverNode, String propertyName, SLExpressionNode valueNode) {
         super(src);
         this.receiverNode = receiverNode;
         this.propertyName = propertyName;
         this.valueNode = valueNode;
+        this.cacheNode = SLWritePropertyCacheNode.create(propertyName);
     }
 
-    @SuppressWarnings("unchecked")
     @Override
     public Object executeGeneric(VirtualFrame frame) {
         Object value = valueNode.executeGeneric(frame);
         Object object = receiverNode.executeGeneric(frame);
-        if (object instanceof Map) {
-            ((Map<Object, Object>) object).put(propertyName, value);
+        if (receiverTypeCondition.profile(SLContext.isSLObject(object))) {
+            cacheNode.executeObject(SLContext.castSLObject(object), value);
         } else {
             throw new SLException("unexpected receiver type");
         }
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java	Tue Nov 18 23:02:58 2014 +0100
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java	Tue Nov 18 12:08:51 2014 +0100
@@ -30,6 +30,7 @@
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.instrument.*;
 import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.object.*;
 import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.sl.*;
 import com.oracle.truffle.sl.builtins.*;
@@ -49,9 +50,12 @@
  * context. Therefore, the context is not a singleton.
  */
 public final class SLContext extends ExecutionContext {
+    private static final Layout LAYOUT = Layout.createLayout();
+
     private final BufferedReader input;
     private final PrintStream output;
     private final SLFunctionRegistry functionRegistry;
+    private final Shape emptyShape;
     private SourceCallback sourceCallback = null;
 
     public SLContext(BufferedReader input, PrintStream output) {
@@ -59,6 +63,8 @@
         this.output = output;
         this.functionRegistry = new SLFunctionRegistry();
         installBuiltins();
+
+        this.emptyShape = LAYOUT.createShape(new ObjectType());
     }
 
     @Override
@@ -180,4 +186,16 @@
         }
         main.getCallTarget().call();
     }
+
+    public DynamicObject createObject() {
+        return LAYOUT.newInstance(emptyShape);
+    }
+
+    public static boolean isSLObject(Object value) {
+        return LAYOUT.getType().isInstance(value);
+    }
+
+    public static DynamicObject castSLObject(Object value) {
+        return LAYOUT.getType().cast(value);
+    }
 }
--- a/mx/suite.py	Tue Nov 18 23:02:58 2014 +0100
+++ b/mx/suite.py	Tue Nov 18 12:08:51 2014 +0100
@@ -1162,7 +1162,10 @@
     "com.oracle.truffle.sl" : {
       "subDir" : "graal",
       "sourceDirs" : ["src"],
-      "dependencies" : ["com.oracle.truffle.api.dsl"],
+      "dependencies" : [
+        "com.oracle.truffle.api.dsl",
+        "com.oracle.truffle.api.object",
+      ],
       "checkstyle" : "com.oracle.graal.graph",
       "javaCompliance" : "1.8",
       "annotationProcessors" : ["com.oracle.truffle.dsl.processor"],