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

com.oracle.truffle.js.builtins.RegExpBuiltins Maven / Gradle / Ivy

/*
 * Copyright (c) 2018, 2024, 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.builtins;

import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.GenerateCached;
import com.oracle.truffle.api.dsl.GenerateInline;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.strings.TruffleString;
import com.oracle.truffle.js.lang.JavaScriptLanguage;
import com.oracle.truffle.js.nodes.JavaScriptBaseNode;
import com.oracle.truffle.js.nodes.cast.JSToStringNode;
import com.oracle.truffle.js.nodes.function.JSBuiltin;
import com.oracle.truffle.js.nodes.function.JSBuiltinNode;
import com.oracle.truffle.js.runtime.Errors;
import com.oracle.truffle.js.runtime.JSContext;
import com.oracle.truffle.js.runtime.JSFrameUtil;
import com.oracle.truffle.js.runtime.JSRealm;
import com.oracle.truffle.js.runtime.Strings;
import com.oracle.truffle.js.runtime.builtins.BuiltinEnum;
import com.oracle.truffle.js.runtime.builtins.JSRegExp;
import com.oracle.truffle.js.runtime.objects.Undefined;
import com.oracle.truffle.js.runtime.util.TRegexUtil;
import com.oracle.truffle.js.runtime.util.TRegexUtil.TRegexCompiledRegexAccessor;
import com.oracle.truffle.js.runtime.util.TRegexUtil.TRegexResultAccessor;

/**
 * Contains builtin methods for {@linkplain JSRegExp}.
 */
public final class RegExpBuiltins extends JSBuiltinsContainer.SwitchEnum {

    public static final JSBuiltinsContainer BUILTINS = new RegExpBuiltins();

    protected RegExpBuiltins() {
        super(JSRegExp.CLASS_NAME, RegExpBuiltin.class);
    }

    public enum RegExpBuiltin implements BuiltinEnum {

        input(0),
        set_input(1, Strings.INPUT),
        lastMatch(0),
        lastParen(0),
        leftContext(0),
        rightContext(0),
        multiline(0),
        $1(0),
        $2(0),
        $3(0),
        $4(0),
        $5(0),
        $6(0),
        $7(0),
        $8(0),
        $9(0);

        private final TruffleString key;
        private final int length;

        RegExpBuiltin(int length) {
            this.key = Strings.fromJavaString(name());
            this.length = length;
        }

        RegExpBuiltin(int length, TruffleString key) {
            this.key = key;
            this.length = length;
        }

        @Override
        public TruffleString getKey() {
            return key;
        }

        @Override
        public int getLength() {
            return length;
        }

        @Override
        public boolean isGetter() {
            return getLength() == 0;
        }

        @Override
        public boolean isSetter() {
            return getLength() == 1;
        }
    }

    @Override
    protected Object createNode(JSContext context, JSBuiltin builtin, boolean construct, boolean newTarget, RegExpBuiltin builtinEnum) {
        switch (builtinEnum) {
            case input:
                return RegExpBuiltinsFactory.JSRegExpStaticResultGetInputNodeGen.create(context, builtin, args().createArgumentNodes(context));
            case set_input:
                return RegExpBuiltinsFactory.JSRegExpStaticResultSetInputNodeGen.create(context, builtin, args().fixedArgs(1).createArgumentNodes(context));
            case lastMatch:
                return RegExpBuiltinsFactory.JSRegExpStaticResultGetGroupNodeGen.create(context, builtin, 0, args().createArgumentNodes(context));
            case lastParen:
                return RegExpBuiltinsFactory.JSRegExpStaticResultLastParenNodeGen.create(context, builtin, args().createArgumentNodes(context));
            case leftContext:
                return RegExpBuiltinsFactory.JSRegExpStaticResultLeftContextNodeGen.create(context, builtin, args().createArgumentNodes(context));
            case rightContext:
                return RegExpBuiltinsFactory.JSRegExpStaticResultRightContextNodeGen.create(context, builtin, args().createArgumentNodes(context));
            case multiline:
                return RegExpBuiltinsFactory.JSRegExpStaticResultMultilineNodeGen.create(context, builtin, args().createArgumentNodes(context));
            case $1:
            case $2:
            case $3:
            case $4:
            case $5:
            case $6:
            case $7:
            case $8:
            case $9:
                return RegExpBuiltinsFactory.JSRegExpStaticResultGetGroupNodeGen.create(context, builtin, builtinEnum.name().charAt(1) - '0', args().createArgumentNodes(context));
            default:
                return null;
        }
    }

    static void checkStaticRegexResultPropertyGet(JSContext context, JSRealm realm, Object thisValue) {
        CompilerAsserts.partialEvaluationConstant(context);
        if (!context.isOptionV8CompatibilityMode()) {
            if (thisValue != realm.getRegExpConstructor() || realm.isRegexResultInvalidated()) {
                throw Errors.createTypeError("Static RegExp result properties cannot be used with subclasses of RegExp.");
            }
        }
    }

    static void checkStaticRegexResultPropertySet(JSContext context, JSRealm realm, Object thisValue) {
        CompilerAsserts.partialEvaluationConstant(context);
        if (!context.isOptionV8CompatibilityMode()) {
            if (thisValue != realm.getRegExpConstructor()) {
                throw Errors.createTypeError("Static RegExp result properties cannot be used with subclasses of RegExp.");
            }
        }
    }

    @GenerateInline
    @GenerateCached(false)
    abstract static class GetStaticRegExpResultNode extends JavaScriptBaseNode {

        GetStaticRegExpResultNode() {
        }

        abstract Object execute(Node node);

        @Specialization
        static Object get(Node node,
                        @Cached TRegexUtil.InvokeExecMethodNode invokeExec) {
            return JSRealm.get(node).getStaticRegexResult(JavaScriptLanguage.get(node).getJSContext(), node, invokeExec);
        }
    }

    abstract static class JSRegExpStaticResultGetInputNode extends JSBuiltinNode {

        JSRegExpStaticResultGetInputNode(JSContext context, JSBuiltin builtin) {
            super(context, builtin);
        }

        @Specialization
        TruffleString getInputProp(VirtualFrame frame) {
            checkStaticRegexResultPropertyGet(getContext(), getRealm(), JSFrameUtil.getThisObj(frame));
            return getRealm().getStaticRegexResultInputString();
        }
    }

    abstract static class JSRegExpStaticResultSetInputNode extends JSBuiltinNode {

        @Child private JSToStringNode toStringNode = JSToStringNode.create();

        JSRegExpStaticResultSetInputNode(JSContext context, JSBuiltin builtin) {
            super(context, builtin);
        }

        @Specialization
        Object setInputProp(VirtualFrame frame, Object val) {
            checkStaticRegexResultPropertySet(getContext(), getRealm(), JSFrameUtil.getThisObj(frame));
            getRealm().setStaticRegexResultInputString(toStringNode.executeString(val));
            return Undefined.instance;
        }
    }

    /**
     * Seems to be a leftover even in the nashorn implementation. QUOTE:
     *
     * 
     * @Getter(where = Where.CONSTRUCTOR, attributes = Attribute.CONSTANT, name = "multiline")
     * public static Object getLastMultiline(final Object self) {
     *     return false; // doesn't ever seem to become true and isn't documented anyhwere
     * }
     * 
*/ abstract static class JSRegExpStaticResultMultilineNode extends JSBuiltinNode { JSRegExpStaticResultMultilineNode(JSContext context, JSBuiltin builtin) { super(context, builtin); } @Specialization(guards = "getContext().isOptionNashornCompatibilityMode()") static boolean getMultilineNashorn() { return false; } @Specialization(guards = "!getContext().isOptionNashornCompatibilityMode()", assumptions = "getContext().getRegExpStaticResultUnusedAssumption()") boolean getMultilineLazy( @Bind("this") Node node, @Cached(inline = true) @Shared @SuppressWarnings("unused") TRegexUtil.InteropReadBooleanMemberNode readIsMatch, @Cached @Shared TRegexUtil.TRegexCompiledRegexSingleFlagAccessorNode getMultilineFlag) { Object compiledRegex = getRealm().getStaticRegexResultCompiledRegex(); if (compiledRegex != null) { return getMultilineFlag.execute(node, compiledRegex, TRegexUtil.Props.Flags.MULTILINE); } else { return false; } } @SuppressWarnings("truffle-static-method") @Specialization(guards = "!getContext().isOptionNashornCompatibilityMode()") boolean getMultilineEager( @Bind("this") Node node, @Cached GetStaticRegExpResultNode getResultNode, @Cached(inline = true) @Shared TRegexUtil.InteropReadBooleanMemberNode readIsMatch, @Cached @Shared TRegexUtil.TRegexCompiledRegexSingleFlagAccessorNode getMultilineFlag) { Object compiledRegex = getRealm().getStaticRegexResultCompiledRegex(); Object result = getResultNode.execute(node); if (TRegexResultAccessor.isMatch(result, node, readIsMatch)) { return getMultilineFlag.execute(node, compiledRegex, TRegexUtil.Props.Flags.MULTILINE); } else { return false; } } } abstract static class JSRegExpStaticResultPropertyNode extends JSBuiltinNode { JSRegExpStaticResultPropertyNode(JSContext context, JSBuiltin builtin) { super(context, builtin); } protected final TruffleString getInput() { return getRealm().getStaticRegexResultOriginalInputString(); } protected final boolean isMatchResult(Object result, TRegexUtil.InteropReadBooleanMemberNode readIsMatch) { return TRegexResultAccessor.isMatch(result, this, readIsMatch); } protected final int groupCount(Object compiledRegex, TRegexUtil.InteropReadIntMemberNode readGroupCount) { return TRegexCompiledRegexAccessor.groupCount(compiledRegex, this, readGroupCount); } protected final int captureGroupStart(Object result, int groupNumber, TRegexUtil.InvokeGetGroupBoundariesMethodNode getStart) { return TRegexResultAccessor.captureGroupStart(result, groupNumber, this, getStart); } protected final int captureGroupEnd(Object result, int groupNumber, TRegexUtil.InvokeGetGroupBoundariesMethodNode getEnd) { return TRegexResultAccessor.captureGroupEnd(result, groupNumber, this, getEnd); } } abstract static class JSRegExpStaticResultGetGroupNode extends JSRegExpStaticResultPropertyNode { private final int groupNumber; JSRegExpStaticResultGetGroupNode(JSContext context, JSBuiltin builtin, int groupNumber) { super(context, builtin); assert groupNumber >= 0; this.groupNumber = groupNumber; } @Specialization TruffleString getGroup(VirtualFrame frame, @Cached TruffleString.SubstringByteIndexNode substringNode, @Cached(inline = true) TRegexUtil.InteropReadBooleanMemberNode readIsMatch, @Cached(inline = true) TRegexUtil.InteropReadIntMemberNode readGroupCount, @Cached(inline = true) TRegexUtil.InvokeGetGroupBoundariesMethodNode getStart, @Cached(inline = true) TRegexUtil.InvokeGetGroupBoundariesMethodNode getEnd, @Cached GetStaticRegExpResultNode getResultNode) { JSRealm realm = getRealm(); checkStaticRegexResultPropertyGet(getContext(), realm, JSFrameUtil.getThisObj(frame)); Object result = getResultNode.execute(this); if (isMatchResult(result, readIsMatch) && groupCount(realm.getStaticRegexResultCompiledRegex(), readGroupCount) > groupNumber) { int start = captureGroupStart(result, groupNumber, getStart); if (start >= 0) { return Strings.substring(getContext(), substringNode, getInput(), start, captureGroupEnd(result, groupNumber, getEnd) - start); } } return Strings.EMPTY_STRING; } } abstract static class JSRegExpStaticResultLastParenNode extends JSRegExpStaticResultPropertyNode { JSRegExpStaticResultLastParenNode(JSContext context, JSBuiltin builtin) { super(context, builtin); } @Specialization TruffleString lastParen(VirtualFrame frame, @Cached TruffleString.SubstringByteIndexNode substringNode, @Cached(inline = true) TRegexUtil.InteropReadBooleanMemberNode readIsMatch, @Cached(inline = true) TRegexUtil.InteropReadIntMemberNode readGroupCount, @Cached(inline = true) TRegexUtil.InvokeGetGroupBoundariesMethodNode getStart, @Cached(inline = true) TRegexUtil.InvokeGetGroupBoundariesMethodNode getEnd, @Cached GetStaticRegExpResultNode getResultNode) { JSRealm realm = getRealm(); checkStaticRegexResultPropertyGet(getContext(), realm, JSFrameUtil.getThisObj(frame)); Object result = getResultNode.execute(this); if (isMatchResult(result, readIsMatch)) { int groupNumber = groupCount(realm.getStaticRegexResultCompiledRegex(), readGroupCount) - 1; if (groupNumber > 0) { int start = captureGroupStart(result, groupNumber, getStart); if (start >= 0) { return Strings.substring(getContext(), substringNode, getInput(), start, captureGroupEnd(result, groupNumber, getEnd) - start); } } } return Strings.EMPTY_STRING; } } abstract static class JSRegExpStaticResultLeftContextNode extends JSRegExpStaticResultPropertyNode { JSRegExpStaticResultLeftContextNode(JSContext context, JSBuiltin builtin) { super(context, builtin); } @Specialization TruffleString leftContext(VirtualFrame frame, @Cached TruffleString.SubstringByteIndexNode substringNode, @Cached(inline = true) TRegexUtil.InteropReadBooleanMemberNode readIsMatch, @Cached(inline = true) TRegexUtil.InvokeGetGroupBoundariesMethodNode getStart, @Cached GetStaticRegExpResultNode getResultNode) { checkStaticRegexResultPropertyGet(getContext(), getRealm(), JSFrameUtil.getThisObj(frame)); Object result = getResultNode.execute(this); if (isMatchResult(result, readIsMatch)) { int start = captureGroupStart(result, 0, getStart); return Strings.substring(getContext(), substringNode, getInput(), 0, start); } else { return Strings.EMPTY_STRING; } } } abstract static class JSRegExpStaticResultRightContextNode extends JSRegExpStaticResultPropertyNode { JSRegExpStaticResultRightContextNode(JSContext context, JSBuiltin builtin) { super(context, builtin); } @Specialization TruffleString rightContext(VirtualFrame frame, @Cached TruffleString.SubstringByteIndexNode substringNode, @Cached(inline = true) TRegexUtil.InteropReadBooleanMemberNode readIsMatch, @Cached(inline = true) TRegexUtil.InvokeGetGroupBoundariesMethodNode getEnd, @Cached GetStaticRegExpResultNode getResultNode) { checkStaticRegexResultPropertyGet(getContext(), getRealm(), JSFrameUtil.getThisObj(frame)); Object result = getResultNode.execute(this); if (isMatchResult(result, readIsMatch)) { int end = captureGroupEnd(result, 0, getEnd); return Strings.substring(getContext(), substringNode, getInput(), end); } else { return Strings.EMPTY_STRING; } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy