package org.graalphp.nodes.function;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.frame.VirtualFrame;
import org.graalphp.exception.PhpUndefFunctionException;
import org.graalphp.nodes.PhpExprNode;
import org.graalphp.parser.ParseScope;
import org.graalphp.types.PhpFunction;

/**
 * Function implementation may only be known after a function call
 * in terms of location in source code.
 * <p>
 * During runtime, this node looks up the implementation of the function.
 *
 * @author abertschi
 */
public final class PhpFunctionLookupNode extends PhpExprNode {

    private final String name;

    // lazy load function because not yet available when this node is created
    @CompilationFinal
    private PhpFunction function;

    private final ParseScope scope;

    public PhpFunctionLookupNode(String name, ParseScope scope) {
        this.name = name;
        this.scope = scope;
    }

    @Override
    public PhpFunction executeGeneric(VirtualFrame frame) {
        if (function == null) {
            // We are about to change a @CompilationFinal field.
            CompilerDirectives.transferToInterpreterAndInvalidate();

            PhpFunction fn = this.scope.resolveFunction(this.name);
            if (fn == null) {
                StringBuilder buf = new StringBuilder()
                        .append("Function ")
                        .append(name)
                        .append(" not found.")
                        .append(" scope: ")
                        .append(scope.getGlobal() == scope ? "<global>" : "<function>");
                throw new PhpUndefFunctionException(buf.toString(), this);
            }
            this.function = fn;
        }
        return function;
    }
}
