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

org.jruby.truffle.language.objects.DefineClassNode Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2013, 2017 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.language.objects;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.api.profiles.ConditionProfile;
import org.jruby.truffle.Layouts;
import org.jruby.truffle.core.klass.ClassNodes;
import org.jruby.truffle.language.RubyConstant;
import org.jruby.truffle.language.RubyGuards;
import org.jruby.truffle.language.RubyNode;
import org.jruby.truffle.language.control.RaiseException;
import org.jruby.truffle.language.dispatch.CallDispatchHeadNode;
import org.jruby.truffle.language.dispatch.DispatchHeadNodeFactory;

public class DefineClassNode extends RubyNode {

    protected final String name;

    @Child private RubyNode superClassNode;
    @Child private RubyNode lexicalParentModule;
    @Child private LookupForExistingModuleNode lookupForExistingModuleNode;
    @Child private CallDispatchHeadNode inheritedNode;

    private final ConditionProfile needToDefineProfile = ConditionProfile.createBinaryProfile();
    private final ConditionProfile noSuperClassSupplied = ConditionProfile.createBinaryProfile();
    private final BranchProfile errorProfile = BranchProfile.create();

    public DefineClassNode(
            String name,
            RubyNode lexicalParent, RubyNode superClass) {
        this.name = name;
        this.lexicalParentModule = lexicalParent;
        this.superClassNode = superClass;
    }

    @Override
    public Object execute(VirtualFrame frame) {
        final Object lexicalParentObject = lexicalParentModule.execute(frame);

        if (!RubyGuards.isRubyModule(lexicalParentObject)) {
            errorProfile.enter();
            throw new RaiseException(coreExceptions().typeErrorIsNotA(lexicalParentObject, "module", this));
        }

        final DynamicObject lexicalParentModule = (DynamicObject) lexicalParentObject;
        final DynamicObject suppliedSuperClass = executeSuperClass(frame);
        final RubyConstant constant = lookupForExistingModule(frame, name, lexicalParentModule);

        final DynamicObject definedClass;

        if (needToDefineProfile.profile(constant == null)) {
            final DynamicObject superClass;
            if (noSuperClassSupplied.profile(suppliedSuperClass == null)) {
                superClass = getContext().getCoreLibrary().getObjectClass();
            } else {
                superClass = suppliedSuperClass;
            }
            definedClass = ClassNodes.createInitializedRubyClass(getContext(), lexicalParentModule, superClass, name);
            callInherited(frame, superClass, definedClass);
        } else {
            if (!RubyGuards.isRubyClass(constant.getValue())) {
                errorProfile.enter();
                throw new RaiseException(coreExceptions().typeErrorIsNotA(constant.getValue(), "class", this));
            }

            definedClass = (DynamicObject) constant.getValue();

            final DynamicObject currentSuperClass = ClassNodes.getSuperClass(definedClass);

            if (suppliedSuperClass != null && currentSuperClass != suppliedSuperClass) { // bug-compat with MRI https://bugs.ruby-lang.org/issues/12367
                errorProfile.enter();
                throw new RaiseException(coreExceptions().superclassMismatch(
                        Layouts.MODULE.getFields(definedClass).getName(), this));
            }
        }

        return definedClass;
    }

    private DynamicObject executeSuperClass(VirtualFrame frame) {
        if (superClassNode == null) {
            return null;
        }
        final Object superClassObject = superClassNode.execute(frame);

        if (!RubyGuards.isRubyClass(superClassObject)) {
            errorProfile.enter();
            throw new RaiseException(coreExceptions().typeError("superclass must be a Class", this));
        }

        final DynamicObject superClass = (DynamicObject) superClassObject;

        if (Layouts.CLASS.getIsSingleton(superClass)) {
            errorProfile.enter();
            throw new RaiseException(coreExceptions().typeError("can't make subclass of virtual class", this));
        }

        return superClass;
    }

    private void callInherited(VirtualFrame frame, DynamicObject superClass, DynamicObject childClass) {
        if (inheritedNode == null) {
            CompilerDirectives.transferToInterpreterAndInvalidate();
            inheritedNode = insert(DispatchHeadNodeFactory.createMethodCallOnSelf());
        }
        inheritedNode.call(frame, superClass, "inherited", childClass);
    }

    private RubyConstant lookupForExistingModule(VirtualFrame frame, String name, DynamicObject lexicalParent) {
        if (lookupForExistingModuleNode == null) {
            CompilerDirectives.transferToInterpreterAndInvalidate();
            lookupForExistingModuleNode = insert(LookupForExistingModuleNodeGen.create(null, null));
        }
        return lookupForExistingModuleNode.executeLookupForExistingModule(frame, name, lexicalParent);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy