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

com.oracle.truffle.js.parser.env.FunctionEnvironment Maven / Gradle / Ivy

There is a newer version: 4.15.102
Show newest version
/*
 * Copyright (c) 2018, 2020, 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.js.parser.env;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;

import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.api.frame.FrameSlotKind;
import com.oracle.truffle.js.nodes.JavaScriptNode;
import com.oracle.truffle.js.nodes.NodeFactory;
import com.oracle.truffle.js.nodes.access.ScopeFrameNode;
import com.oracle.truffle.js.nodes.control.BreakTarget;
import com.oracle.truffle.js.nodes.control.ContinueTarget;
import com.oracle.truffle.js.runtime.JSContext;

public class FunctionEnvironment extends Environment {
    private static final String RETURN_SLOT_IDENTIFIER = "";
    static final String ARGUMENTS_SLOT_IDENTIFIER = "";
    static final String THIS_SLOT_IDENTIFIER = "";
    static final String SUPER_SLOT_IDENTIFIER = "";
    static final String NEW_TARGET_SLOT_IDENTIFIER = "";
    static final String YIELD_VALUE_SLOT_IDENTIFIER = "";
    static final String ASYNC_CONTEXT_SLOT_IDENTIFIER = "";
    static final String ASYNC_RESULT_SLOT_IDENTIFIER = "";
    private static final String YIELD_RESULT_SLOT_IDENTIFIER = "";
    static final String DYNAMIC_SCOPE_IDENTIFIER = "";

    private final FunctionEnvironment parent;
    private final FrameDescriptor frameDescriptor;
    private final List parameters;
    private final boolean isStrictMode;

    private FrameSlot argumentsSlot;
    private FrameSlot returnSlot;
    private FrameSlot thisSlot;
    private FrameSlot superSlot;
    private FrameSlot newTargetSlot;
    private FrameSlot dynamicScopeSlot;

    private String functionName = "";
    private String internalFunctionName = "";
    private boolean isNamedExpression;
    private boolean needsParentFrame;
    private boolean frozen;

    private int breakNodeCount;
    private int continueNodeCount;
    private boolean hasReturn;
    private boolean hasYield;
    private boolean hasAwait;

    private List jumpTargetStack;
    private boolean directArgumentsAccess;

    private final boolean isGlobal;
    private final boolean isEval;
    private final boolean isDirectEval;
    private final boolean isArrowFunction;
    private final boolean isGeneratorFunction;
    private final boolean isDerivedConstructor;
    private final boolean isAsyncFunction;
    // Synthetic arguments are declared externally, e.g. when parsing with arguments.
    private final boolean hasSyntheticArguments;
    private boolean hasRestParameter;
    private boolean simpleParameterList = true;
    private boolean isDynamicallyScoped;
    private final boolean inDirectEval;

    public FunctionEnvironment(Environment parent, NodeFactory factory, JSContext context,
                    boolean isStrictMode, boolean isEval, boolean isDirectEval, boolean isArrowFunction, boolean isGeneratorFunction, boolean isDerivedConstructor, boolean isAsyncFunction,
                    boolean isGlobal, boolean hasSyntheticArguments) {
        super(parent, factory, context);
        this.isDirectEval = isDirectEval;
        this.isAsyncFunction = isAsyncFunction;
        this.isStrictMode = isStrictMode;
        this.isEval = isEval;
        this.isArrowFunction = isArrowFunction;
        this.isGeneratorFunction = isGeneratorFunction;
        this.isDerivedConstructor = isDerivedConstructor;
        this.isGlobal = isGlobal;
        this.hasSyntheticArguments = hasSyntheticArguments;
        this.parent = parent == null ? null : parent.function();

        this.frameDescriptor = factory.createFrameDescriptor();
        this.parameters = new ArrayList<>();
        this.inDirectEval = isDirectEval || (parent != null && parent.function() != null && parent.function().inDirectEval());
    }

    @Override
    public FunctionEnvironment function() {
        return this;
    }

    @Override
    public FrameSlot declareLocalVar(Object name) {
        return getFunctionFrameDescriptor().findOrAddFrameSlot(name, FrameSlotKind.Illegal);
    }

    @Override
    public FrameSlot declareVar(Object name) {
        if (isCallerContextEval()) {
            return getParentFunction().declareVar(name);
        }
        if (frozen && getFunctionFrameDescriptor().findFrameSlot(name) == null) {
            throw errorFrozenEnv();
        }
        return declareLocalVar(name);
    }

    public FrameSlot reserveArgumentsSlot() {
        if (argumentsSlot == null) {
            argumentsSlot = declareVar(ARGUMENTS_SLOT_IDENTIFIER);
        }
        return argumentsSlot;
    }

    public FrameSlot getReturnSlot() {
        if (returnSlot == null) {
            returnSlot = declareLocalVar(RETURN_SLOT_IDENTIFIER);
        }
        return returnSlot;
    }

    public void reserveThisSlot() {
        if (thisSlot == null) {
            thisSlot = declareVar(THIS_SLOT_IDENTIFIER);
        }
    }

    public void reserveNewTargetSlot() {
        if (newTargetSlot == null) {
            newTargetSlot = declareVar(NEW_TARGET_SLOT_IDENTIFIER);
        }
    }

    public FrameSlot getAsyncResultSlot() {
        return declareLocalVar(ASYNC_RESULT_SLOT_IDENTIFIER);
    }

    public FrameSlot getAsyncContextSlot() {
        return declareLocalVar(ASYNC_CONTEXT_SLOT_IDENTIFIER);
    }

    public FrameSlot getYieldResultSlot() {
        return declareLocalVar(YIELD_RESULT_SLOT_IDENTIFIER);
    }

    public FrameSlot getThisSlot() {
        return thisSlot;
    }

    public FrameSlot reserveDynamicScopeSlot() {
        if (dynamicScopeSlot == null) {
            dynamicScopeSlot = declareLocalVar(DYNAMIC_SCOPE_IDENTIFIER);
        }
        return dynamicScopeSlot;
    }

    public boolean isEval() {
        return isEval;
    }

    public boolean isArrowFunction() {
        return isArrowFunction;
    }

    public boolean isGeneratorFunction() {
        return isGeneratorFunction;
    }

    @Override
    protected FrameSlot findBlockFrameSlot(String name) {
        return getFunctionFrameDescriptor().findFrameSlot(name);
    }

    @Override
    public boolean hasLocalVar(String name) {
        return getFunctionFrameDescriptor().getIdentifiers().contains(name);
    }

    private  T pushJumpTarget(T target) {
        if (jumpTargetStack == null) {
            jumpTargetStack = new ArrayList<>(4);
        }
        jumpTargetStack.add(target);
        return target;
    }

    private void popJumpTarget(BreakTarget target) {
        assert jumpTargetStack != null && jumpTargetStack.get(jumpTargetStack.size() - 1) == target;
        jumpTargetStack.remove(jumpTargetStack.size() - 1);
    }

    public JumpTargetCloseable pushContinueTarget(Object label) {
        ContinueTarget target = ContinueTarget.forLoop(label, -1);
        pushJumpTarget(target);
        return new JumpTargetCloseable<>(target);
    }

    public JumpTargetCloseable pushBreakTarget(Object label) {
        BreakTarget target = label == null ? BreakTarget.forSwitch() : BreakTarget.forLabel(label, -1);
        pushJumpTarget(target);
        return new JumpTargetCloseable<>(target);
    }

    public BreakTarget findBreakTarget(Object label) {
        breakNodeCount++;
        return findJumpTarget(label, BreakTarget.class, true);
    }

    public ContinueTarget findContinueTarget(Object label) {
        continueNodeCount++;
        return findJumpTarget(label, ContinueTarget.class, false);
    }

    private  T findJumpTarget(Object label, Class targetClass, boolean direct) {
        T applicableTarget = null;
        for (ListIterator iterator = jumpTargetStack.listIterator(jumpTargetStack.size()); iterator.hasPrevious();) {
            BreakTarget target = iterator.previous();
            if (direct || label == null) {
                // ignore label or label directly on target
                if (label == null || label.equals(target.getLabel())) {
                    if (targetClass.isInstance(target)) {
                        return targetClass.cast(target);
                    }
                }
            } else {
                assert !direct;
                // label is indirectly associated with last applicable target
                if (targetClass.isInstance(target)) {
                    applicableTarget = targetClass.cast(target);
                }
                if (label.equals(target.getLabel())) {
                    assert applicableTarget != null : "Illegal or duplicate label"; // SyntaxError
                    return applicableTarget;
                }
            }
        }
        throw new NoSuchElementException("jump target not found");
    }

    public boolean hasReturn() {
        return hasReturn;
    }

    public void addReturn() {
        hasReturn = true;
    }

    public boolean hasAwait() {
        return hasAwait;
    }

    public void addAwait() {
        hasAwait = true;
    }

    public boolean hasYield() {
        return hasYield;
    }

    public void addYield() {
        hasYield = true;
    }

    public void setDirectArgumentsAccess(boolean directArgumentsAccess) {
        this.directArgumentsAccess = directArgumentsAccess;
    }

    public boolean isDirectArgumentsAccess() {
        return directArgumentsAccess;
    }

    public final int getParameterCount() {
        return parameters.size();
    }

    public final List getParameters() {
        return parameters;
    }

    public final void declareParameter(String name) {
        parameters.add(declareLocalVar(name));
    }

    protected final boolean isParam(FrameSlot slot) {
        return parameters.contains(slot);
    }

    protected final int getParameterIndex(FrameSlot slot) {
        for (int i = parameters.size() - 1; i >= 0; --i) {
            if (parameters.get(i) == slot) {
                return i;
            }
        }
        return -1;
    }

    public final boolean isParameter(String name) {
        for (int i = 0; i < parameters.size(); i++) {
            if (name.equals(parameters.get(i).getIdentifier())) {
                return true;
            }
        }
        return false;
    }

    public final String getFunctionName() {
        return functionName;
    }

    public final void setFunctionName(String functionName) {
        this.functionName = functionName;
    }

    public final String getInternalFunctionName() {
        return internalFunctionName;
    }

    public final void setInternalFunctionName(String internalFunctionName) {
        this.internalFunctionName = internalFunctionName;
    }

    public final void setNamedFunctionExpression(boolean isNamedExpression) {
        this.isNamedExpression = isNamedExpression;
    }

    protected final boolean isNamedFunctionExpression() {
        return isNamedExpression;
    }

    public final boolean needsParentFrame() {
        return needsParentFrame;
    }

    public final void setNeedsParentFrame(boolean needsParentFrame) {
        if (frozen && needsParentFrame != this.needsParentFrame) {
            throw errorFrozenEnv();
        }
        this.needsParentFrame = needsParentFrame;
    }

    private static RuntimeException errorFrozenEnv() {
        return new IllegalStateException("frozen function environment cannot be mutated");
    }

    public void freeze() {
        if (this.frozen) {
            return;
        }
        this.frozen = true;
    }

    public boolean isFrozen() {
        return frozen;
    }

    public boolean isDeepFrozen() {
        return isFrozen() && (getParentFunction() == null || getParentFunction().isDeepFrozen());
    }

    public final boolean hasArgumentsSlot() {
        return argumentsSlot != null;
    }

    protected final FrameSlot getArgumentsSlot() {
        return argumentsSlot;
    }

    @Override
    public final FrameDescriptor getFunctionFrameDescriptor() {
        return frameDescriptor;
    }

    @Override
    public final boolean isStrictMode() {
        return this.isStrictMode;
    }

    public final FunctionEnvironment getParentFunction() {
        return parent;
    }

    public final FunctionEnvironment getParentFunction(int level) {
        assert level >= 0;
        if (level == 0) {
            return this;
        } else {
            return parent.getParentFunction(level - 1);
        }
    }

    public final FunctionEnvironment getNonArrowParentFunction() {
        if (isArrowFunction() || isDirectEval()) {
            return getParentFunction().getNonArrowParentFunction();
        }
        return this;
    }

    @Override
    public FrameSlot[] getParentSlots() {
        return ScopeFrameNode.EMPTY_FRAME_SLOT_ARRAY;
    }

    public boolean isGlobal() {
        return isGlobal;
    }

    public boolean hasSyntheticArguments() {
        return this.hasSyntheticArguments;
    }

    public boolean returnsLastStatementResult() {
        return isGlobal() || isDirectEval() || hasSyntheticArguments();
    }

    public void setIsDynamicallyScoped(boolean isDynamicallyScoped) {
        this.isDynamicallyScoped = isDynamicallyScoped;
    }

    /**
     * Function is dynamically scope because it's non-strict and has a direct eval call.
     */
    @Override
    public boolean isDynamicallyScoped() {
        return isDynamicallyScoped;
    }

    @Override
    public boolean isDynamicScopeContext() {
        return isDynamicallyScoped() || isCallerContextEval() || super.isDynamicScopeContext();
    }

    @Override
    public Environment getVariableEnvironment() {
        if (isCallerContextEval()) {
            return getParentFunction().getVariableEnvironment();
        }
        return this;
    }

    public class JumpTargetCloseable implements AutoCloseable {

        private final T target;
        private final int prevBreakCount = breakNodeCount;
        private final int prevContinueCount = continueNodeCount;

        protected JumpTargetCloseable(T target) {
            this.target = target;
        }

        public T getTarget() {
            return target;
        }

        @Override
        public void close() {
            popJumpTarget(target);
        }

        private boolean hasBreak() {
            return breakNodeCount != prevBreakCount;
        }

        private boolean hasContinue() {
            return continueNodeCount != prevContinueCount;
        }

        public JavaScriptNode wrapContinueTargetNode(JavaScriptNode child) {
            boolean hasContinue = hasContinue();
            return hasContinue ? factory.createContinueTarget(child, (ContinueTarget) target) : child;
        }

        public JavaScriptNode wrapBreakTargetNode(JavaScriptNode child) {
            assert target.getLabel() == null;
            boolean hasBreak = hasBreak();
            return hasBreak ? factory.createDirectBreakTarget(child) : child;
        }

        public JavaScriptNode wrapLabelBreakTargetNode(JavaScriptNode child) {
            assert target.getLabel() != null;
            boolean hasBreak = hasBreak();
            return hasBreak ? factory.createLabel(child, target) : child;
        }
    }

    public boolean isDirectEval() {
        return isDirectEval;
    }

    public boolean isIndirectEval() {
        return isEval() && !isDirectEval();
    }

    public boolean isCallerContextEval() {
        return isDirectEval() && !isStrictMode() && !isGlobal();
    }

    public boolean inDirectEval() {
        return inDirectEval;
    }

    public void reserveSuperSlot() {
        if (superSlot == null) {
            superSlot = declareVar(SUPER_SLOT_IDENTIFIER);
        }
    }

    public FrameSlot getSuperSlot() {
        return superSlot;
    }

    public FrameSlot getNewTargetSlot() {
        return newTargetSlot;
    }

    public void setRestParameter(boolean restParameter) {
        this.hasRestParameter = restParameter;
    }

    public boolean hasRestParameter() {
        return hasRestParameter;
    }

    public void setSimpleParameterList(boolean simpleParameterList) {
        this.simpleParameterList = simpleParameterList;
    }

    public boolean hasSimpleParameterList() {
        return simpleParameterList;
    }

    public int getLeadingArgumentCount() {
        return getNewTargetSlot() != null ? 1 : 0;
    }

    public int getTrailingArgumentCount() {
        return 0;
    }

    public boolean isDerivedConstructor() {
        return isDerivedConstructor;
    }

    /**
     * Returns the number of function levels to skip to reach the function with the [[ThisValue]].
     * Loosely resembles GetThisEnvironment(), but we need to consider eval functions, too.
     */
    public int getThisFunctionLevel() {
        int level = 0;
        for (FunctionEnvironment currentFunction = this; currentFunction.isArrowFunction() || currentFunction.isDirectEval(); currentFunction = currentFunction.getParentFunction(), level++) {
            currentFunction.setNeedsParentFrame(true);
        }
        return level;
    }

    /**
     * Returns the number of frame levels to skip to reach the outermost function (module/script).
     */
    public int getOutermostFunctionLevel() {
        int level = 0;
        for (FunctionEnvironment currentFunction = this; currentFunction.getParentFunction() != null; currentFunction = currentFunction.getParentFunction(), level++) {
            currentFunction.setNeedsParentFrame(true);
        }
        return level;
    }

    public boolean isAsyncFunction() {
        return isAsyncFunction;
    }

    public boolean isAsyncGeneratorFunction() {
        return isAsyncFunction && isGeneratorFunction;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy