RubyBasicObject.java
/*
* Copyright (c) 2013, 2014 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.runtime.core;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.object.Layout;
import com.oracle.truffle.api.object.Property;
import com.oracle.truffle.api.object.Shape;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.objects.Allocator;
import org.jruby.truffle.runtime.InternalName;
import org.jruby.truffle.runtime.ModuleOperations;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.RubyOperations;
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.subsystems.ObjectSpaceManager;
import java.util.Map;
/**
* Represents the Ruby {@code BasicObject} class - the root of the Ruby class hierarchy.
*/
public class RubyBasicObject {
public static final InternalName OBJECT_ID_IDENTIFIER = new InternalName("object_id");
public static final InternalName TAINTED_IDENTIFIER = new InternalName("tainted?");
public static final Layout LAYOUT = Layout.createLayout(Layout.INT_TO_LONG);
private final DynamicObject dynamicObject;
/** The class of the object, not a singleton class. */
@CompilationFinal protected RubyClass logicalClass;
/** Either the singleton class if it exists or the logicalClass. */
@CompilationFinal protected RubyClass metaClass;
private boolean frozen = false;
public RubyBasicObject(RubyClass rubyClass) {
this(rubyClass, rubyClass.getContext());
}
public RubyBasicObject(RubyClass rubyClass, RubyContext context) {
dynamicObject = LAYOUT.newInstance(context.getEmptyShape());
if (rubyClass != null) {
unsafeSetLogicalClass(rubyClass);
}
}
public boolean hasNoSingleton() {
return false;
}
public boolean hasClassAsSingleton() {
return false;
}
public boolean isFrozen() {
return frozen;
}
public void freeze() {
frozen = true;
}
public void checkFrozen(Node currentNode) {
if (frozen) {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(getContext().getCoreLibrary().frozenError(getLogicalClass().getName(), currentNode));
}
}
public RubyClass getMetaClass() {
return metaClass;
}
public RubyClass getSingletonClass(Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
if (hasNoSingleton()) {
throw new RaiseException(getContext().getCoreLibrary().typeErrorCantDefineSingleton(currentNode));
}
if (hasClassAsSingleton() || metaClass.isSingleton()) {
return metaClass;
}
final RubyClass logicalClass = metaClass;
metaClass = RubyClass.createSingletonClassOfObject(getContext(), logicalClass,
String.format("#<Class:#<%s:0x%x>>", logicalClass.getName(), getObjectID()));
if (isFrozen()) {
metaClass.freeze();
}
return metaClass;
}
@CompilerDirectives.TruffleBoundary
public long getObjectID() {
// TODO(CS): we should specialise on reading this in the #object_id method and anywhere else it's used
Property property = dynamicObject.getShape().getProperty(OBJECT_ID_IDENTIFIER);
if (property != null) {
return (long) property.get(dynamicObject, false);
}
final long objectID = getContext().getNextObjectID();
dynamicObject.define(OBJECT_ID_IDENTIFIER, objectID, 0);
return objectID;
}
@CompilerDirectives.TruffleBoundary
public void setInstanceVariables(Map<Object, Object> instanceVariables) {
RubyNode.notDesignedForCompilation();
assert instanceVariables != null;
getOperations().setInstanceVariables(this, instanceVariables);
}
public Map<Object, Object> getInstanceVariables() {
RubyNode.notDesignedForCompilation();
return getOperations().getInstanceVariables(this);
}
public Object[] getFieldNames() {
return getOperations().getFieldNames(this);
}
public void extend(RubyModule module, RubyNode currentNode) {
RubyNode.notDesignedForCompilation();
getSingletonClass(currentNode).include(currentNode, module);
}
public void unsafeSetLogicalClass(RubyClass newLogicalClass) {
assert logicalClass == null;
logicalClass = newLogicalClass;
metaClass = newLogicalClass;
}
//public void unsafeSetMetaClass(RubyClass newMetaClass) {
// assert metaClass == null;
// metaClass = newMetaClass;
//}
public Object getInstanceVariable(String name) {
RubyNode.notDesignedForCompilation();
final Object value = getOperations().getInstanceVariable(this, name);
if (value == null) {
return getContext().getCoreLibrary().getNilObject();
} else {
return value;
}
}
public boolean isFieldDefined(String name) {
return getOperations().isFieldDefined(this, name);
}
public boolean isTrue() {
return true;
}
public void visitObjectGraph(ObjectSpaceManager.ObjectGraphVisitor visitor) {
if (visitor.visit(this)) {
metaClass.visitObjectGraph(visitor);
for (Object instanceVariable : getOperations().getInstanceVariables(this).values()) {
if (instanceVariable instanceof RubyBasicObject) {
((RubyBasicObject) instanceVariable).visitObjectGraph(visitor);
}
}
visitObjectGraphChildren(visitor);
}
}
public void visitObjectGraphChildren(ObjectSpaceManager.ObjectGraphVisitor visitor) {
}
public boolean isNumeric() {
return ModuleOperations.assignableTo(this.getMetaClass(), getContext().getCoreLibrary().getNumericClass());
}
public RubyContext getContext() {
return logicalClass.getContext();
}
public Shape getObjectLayout() {
return dynamicObject.getShape();
}
public RubyOperations getOperations() {
return (RubyOperations) dynamicObject.getShape().getObjectType();
}
public RubyClass getLogicalClass() {
return logicalClass;
}
public DynamicObject getDynamicObject() {
return dynamicObject;
}
public static class BasicObjectAllocator implements Allocator {
// TODO(CS): why on earth is this a boundary? Seems like a really bad thing.
@CompilerDirectives.TruffleBoundary
@Override
public RubyBasicObject allocate(RubyContext context, RubyClass rubyClass, RubyNode currentNode) {
return new RubyBasicObject(rubyClass);
}
}
}