package com.oracle.truffle.js.runtime.util;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.ArityException;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnknownIdentifierException;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.interop.UnsupportedTypeException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.js.runtime.objects.Undefined;
import com.oracle.truffle.js.runtime.util.TRegexUtil.Props.CompiledRegex;
import com.oracle.truffle.js.runtime.util.TRegexUtil.Props.RegexEngine;
import com.oracle.truffle.js.runtime.util.TRegexUtilFactory.CompileRegexNodeGen;
import com.oracle.truffle.js.runtime.util.TRegexUtilFactory.InteropIsMemberReadableNodeGen;
import com.oracle.truffle.js.runtime.util.TRegexUtilFactory.InteropIsNullNodeGen;
import com.oracle.truffle.js.runtime.util.TRegexUtilFactory.InteropReadBooleanMemberNodeGen;
import com.oracle.truffle.js.runtime.util.TRegexUtilFactory.InteropReadIntMemberNodeGen;
import com.oracle.truffle.js.runtime.util.TRegexUtilFactory.InteropReadMemberNodeGen;
import com.oracle.truffle.js.runtime.util.TRegexUtilFactory.InteropReadStringMemberNodeGen;
import com.oracle.truffle.js.runtime.util.TRegexUtilFactory.InvokeExecMethodNodeGen;
import com.oracle.truffle.js.runtime.util.TRegexUtilFactory.InvokeGetGroupBoundariesMethodNodeGen;
import com.oracle.truffle.js.runtime.util.TRegexUtilFactory.ValidateRegexNodeGen;
public final class TRegexUtil {
private TRegexUtil() {
}
public static final class Props {
private Props() {
}
public static final class RegexEngine {
private RegexEngine() {
}
public static final String VALIDATE = "validate";
}
public static final class CompiledRegex {
private CompiledRegex() {
}
public static final String PATTERN = "pattern";
public static final String FLAGS = "flags";
public static final String EXEC = "exec";
public static final String GROUP_COUNT = "groupCount";
public static final String GROUPS = "groups";
}
public static final class Flags {
private Flags() {
}
public static final String SOURCE = "source";
public static final String GLOBAL = "global";
public static final String MULTILINE = "multiline";
public static final String IGNORE_CASE = "ignoreCase";
public static final String STICKY = "sticky";
public static final String UNICODE = "unicode";
public static final String DOT_ALL = "dotAll";
}
public static final class RegexResult {
private RegexResult() {
}
public static final String IS_MATCH = "isMatch";
public static final String GET_START = "getStart";
public static final String GET_END = "getEnd";
}
}
private static final String NUMBER_OF_REGEX_RESULT_TYPES = "9";
public static final class Constants {
private Constants() {
}
public static final int CAPTURE_GROUP_NO_MATCH = -1;
}
@GenerateUncached
public abstract static class InteropIsNullNode extends Node {
public abstract boolean execute(Object obj);
@Specialization(limit = "3")
static boolean read(Object obj, @CachedLibrary("obj") InteropLibrary objs) {
return objs.isNull(obj);
}
public static InteropIsNullNode create() {
return InteropIsNullNodeGen.create();
}
public static InteropIsNullNode getUncached() {
return InteropIsNullNodeGen.getUncached();
}
}
@GenerateUncached
public abstract static class InteropIsMemberReadableNode extends Node {
public abstract boolean execute(Object obj, String key);
@Specialization(limit = NUMBER_OF_REGEX_RESULT_TYPES)
static boolean read(Object obj, String key, @CachedLibrary("obj") InteropLibrary objs) {
return objs.isMemberReadable(obj, key);
}
public static InteropIsMemberReadableNode create() {
return InteropIsMemberReadableNodeGen.create();
}
}
@GenerateUncached
public abstract static class InteropReadMemberNode extends Node {
public abstract Object execute(Object obj, String key);
@Specialization(guards = "objs.isMemberReadable(obj, key)", limit = NUMBER_OF_REGEX_RESULT_TYPES)
static Object read(Object obj, String key, @CachedLibrary("obj") InteropLibrary objs) {
try {
return objs.readMember(obj, key);
} catch (UnsupportedMessageException | UnknownIdentifierException e) {
CompilerDirectives.transferToInterpreter();
throw new RuntimeException(e);
}
}
public static InteropReadMemberNode create() {
return InteropReadMemberNodeGen.create();
}
public static InteropReadMemberNode getUncached() {
return InteropReadMemberNodeGen.getUncached();
}
}
@GenerateUncached
public abstract static class InteropReadIntMemberNode extends Node {
public abstract int execute(Object obj, String key);
@Specialization(guards = "objs.isMemberReadable(obj, key)", limit = NUMBER_OF_REGEX_RESULT_TYPES)
static int read(Object obj, String key, @Cached InteropToIntNode coerceNode, @CachedLibrary("obj") InteropLibrary objs) {
try {
return coerceNode.execute(objs.readMember(obj, key));
} catch (UnsupportedMessageException | UnknownIdentifierException e) {
CompilerDirectives.transferToInterpreter();
throw new RuntimeException(e);
}
}
public static InteropReadIntMemberNode create() {
return InteropReadIntMemberNodeGen.create();
}
public static InteropReadIntMemberNode getUncached() {
return InteropReadIntMemberNodeGen.getUncached();
}
}
@GenerateUncached
public abstract static class InteropReadBooleanMemberNode extends Node {
public abstract boolean execute(Object obj, String key);
@Specialization(guards = "objs.isMemberReadable(obj, key)", limit = NUMBER_OF_REGEX_RESULT_TYPES)
static boolean read(Object obj, String key, @Cached InteropToBooleanNode coerceNode, @CachedLibrary("obj") InteropLibrary objs) {
try {
return coerceNode.execute(objs.readMember(obj, key));
} catch (UnsupportedMessageException | UnknownIdentifierException e) {
CompilerDirectives.transferToInterpreter();
throw new RuntimeException(e);
}
}
public static InteropReadBooleanMemberNode create() {
return InteropReadBooleanMemberNodeGen.create();
}
public static InteropReadBooleanMemberNode getUncached() {
return InteropReadBooleanMemberNodeGen.getUncached();
}
}
@GenerateUncached
public abstract static class InteropReadStringMemberNode extends Node {
public abstract String execute(Object obj, String key);
@Specialization(guards = "objs.isMemberReadable(obj, key)", limit = "3")
static String read(Object obj, String key, @Cached InteropToStringNode coerceNode, @CachedLibrary("obj") InteropLibrary objs) {
try {
return coerceNode.execute(objs.readMember(obj, key));
} catch (UnsupportedMessageException | UnknownIdentifierException e) {
CompilerDirectives.transferToInterpreter();
throw new RuntimeException(e);
}
}
public static InteropReadStringMemberNode create() {
return InteropReadStringMemberNodeGen.create();
}
public static InteropReadStringMemberNode getUncached() {
return InteropReadStringMemberNodeGen.getUncached();
}
}
@GenerateUncached
public abstract static class InteropToBooleanNode extends Node {
public abstract boolean execute(Object obj);
@Specialization
static boolean coerceDirect(boolean obj) {
return obj;
}
@Specialization(guards = "objs.isBoolean(obj)", limit = "3")
static boolean coerce(Object obj, @CachedLibrary("obj") InteropLibrary objs) {
try {
return objs.asBoolean(obj);
} catch (UnsupportedMessageException e) {
CompilerDirectives.transferToInterpreter();
throw new RuntimeException(e);
}
}
}
@GenerateUncached
public abstract static class InteropToIntNode extends Node {
public abstract int execute(Object obj);
@Specialization
static int coerceDirect(int obj) {
return obj;
}
@Specialization(guards = "objs.fitsInInt(obj)", limit = "3")
static int coerce(Object obj, @CachedLibrary("obj") InteropLibrary objs) {
try {
return objs.asInt(obj);
} catch (UnsupportedMessageException e) {
CompilerDirectives.transferToInterpreter();
throw new RuntimeException(e);
}
}
}
@GenerateUncached
public abstract static class InteropToStringNode extends Node {
public abstract String execute(Object obj);
@Specialization
static String coerceDirect(String obj) {
return obj;
}
@Specialization(guards = "objs.isString(obj)", limit = "3")
static String coerce(Object obj, @CachedLibrary("obj") InteropLibrary objs) {
try {
return objs.asString(obj);
} catch (UnsupportedMessageException e) {
CompilerDirectives.transferToInterpreter();
throw new RuntimeException(e);
}
}
}
@ImportStatic(CompiledRegex.class)
@GenerateUncached
public abstract static class InvokeExecMethodNode extends Node {
public abstract Object execute(Object compiledRegex, String input, long fromIndex);
@Specialization(guards = "objs.isMemberInvocable(compiledRegex, EXEC)", limit = "3")
static Object exec(Object compiledRegex, String input, long fromIndex,
@CachedLibrary("compiledRegex") InteropLibrary objs) {
try {
return objs.invokeMember(compiledRegex, CompiledRegex.EXEC, input, fromIndex);
} catch (UnsupportedMessageException | UnsupportedTypeException | ArityException | UnknownIdentifierException e) {
CompilerDirectives.transferToInterpreter();
throw new RuntimeException(e);
}
}
public static InvokeExecMethodNode create() {
return InvokeExecMethodNodeGen.create();
}
public static InvokeExecMethodNode getUncached() {
return InvokeExecMethodNodeGen.getUncached();
}
}
@GenerateUncached
public abstract static class InvokeGetGroupBoundariesMethodNode extends Node {
public abstract int execute(Object regexResult, String method, int groupNumber);
@Specialization(guards = "objs.isMemberInvocable(regexResult, method)", limit = NUMBER_OF_REGEX_RESULT_TYPES)
static int exec(Object regexResult, String method, int groupNumber,
@CachedLibrary("regexResult") InteropLibrary objs,
@Cached InteropToIntNode toIntNode) {
try {
return toIntNode.execute(objs.invokeMember(regexResult, method, groupNumber));
} catch (UnsupportedMessageException | UnsupportedTypeException | ArityException | UnknownIdentifierException e) {
CompilerDirectives.transferToInterpreter();
throw new RuntimeException(e);
}
}
public static InvokeGetGroupBoundariesMethodNode create() {
return InvokeGetGroupBoundariesMethodNodeGen.create();
}
public static InvokeGetGroupBoundariesMethodNode getUncached() {
return InvokeGetGroupBoundariesMethodNodeGen.getUncached();
}
}
@GenerateUncached
public abstract static class CompileRegexNode extends Node {
public abstract Object execute(Object regexCompiler, String pattern, String flags);
@Specialization(guards = "objs.isExecutable(regexCompiler)", limit = "3")
static Object exec(Object regexCompiler, String pattern, String flags,
@CachedLibrary("regexCompiler") InteropLibrary objs) {
try {
return objs.execute(regexCompiler, pattern, flags);
} catch (UnsupportedMessageException | UnsupportedTypeException | ArityException e) {
CompilerDirectives.transferToInterpreter();
throw new RuntimeException(e);
}
}
public static CompileRegexNode create() {
return CompileRegexNodeGen.create();
}
public static CompileRegexNode getUncached() {
return CompileRegexNodeGen.getUncached();
}
}
@ImportStatic(RegexEngine.class)
@GenerateUncached
public abstract static class ValidateRegexNode extends Node {
public abstract Object execute(Object regexEngine, String pattern, String flags);
@Specialization(guards = "objs.isMemberInvocable(regexEngine, VALIDATE)", limit = "3")
static Object exec(Object regexEngine, String pattern, String flags,
@CachedLibrary("regexEngine") InteropLibrary objs) {
try {
return objs.invokeMember(regexEngine, RegexEngine.VALIDATE, pattern, flags);
} catch (UnsupportedMessageException | UnsupportedTypeException | ArityException | UnknownIdentifierException e) {
CompilerDirectives.transferToInterpreter();
throw new RuntimeException(e);
}
}
public static ValidateRegexNode create() {
return ValidateRegexNodeGen.create();
}
public static ValidateRegexNode getUncached() {
return ValidateRegexNodeGen.getUncached();
}
}
public static final class TRegexCompiledRegexAccessor extends Node {
@Child private InteropReadStringMemberNode readPatternNode;
@Child private InteropReadMemberNode readFlagsNode;
@Child private InvokeExecMethodNode invokeExecMethodNode;
@Child private InteropReadIntMemberNode readGroupCountNode;
@Child private InteropReadMemberNode readGroupsNode;
private TRegexCompiledRegexAccessor() {
}
public static TRegexCompiledRegexAccessor create() {
return new TRegexCompiledRegexAccessor();
}
public String pattern(Object compiledRegexObject) {
return getReadPatternNode().execute(compiledRegexObject, Props.CompiledRegex.PATTERN);
}
public Object flags(Object compiledRegexObject) {
return getReadFlagsNode().execute(compiledRegexObject, Props.CompiledRegex.FLAGS);
}
public Object exec(Object compiledRegexObject, String input, long fromIndex) {
return getInvokeExecMethodNode().execute(compiledRegexObject, input, fromIndex);
}
public int groupCount(Object regexResultObject) {
return getReadGroupCountNode().execute(regexResultObject, Props.CompiledRegex.GROUP_COUNT);
}
public Object namedCaptureGroups(Object compiledRegexObject) {
return getReadGroupsNode().execute(compiledRegexObject, Props.CompiledRegex.GROUPS);
}
private InteropReadStringMemberNode getReadPatternNode() {
if (readPatternNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
readPatternNode = insert(InteropReadStringMemberNode.create());
}
return readPatternNode;
}
private InteropReadMemberNode getReadFlagsNode() {
if (readFlagsNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
readFlagsNode = insert(InteropReadMemberNode.create());
}
return readFlagsNode;
}
private InvokeExecMethodNode getInvokeExecMethodNode() {
if (invokeExecMethodNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
invokeExecMethodNode = insert(InvokeExecMethodNode.create());
}
return invokeExecMethodNode;
}
private InteropReadIntMemberNode getReadGroupCountNode() {
if (readGroupCountNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
readGroupCountNode = insert(InteropReadIntMemberNode.create());
}
return readGroupCountNode;
}
private InteropReadMemberNode getReadGroupsNode() {
if (readGroupsNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
readGroupsNode = insert(InteropReadMemberNode.create());
}
return readGroupsNode;
}
}
public static final class TRegexNamedCaptureGroupsAccessor extends Node {
@Child private InteropIsNullNode isNullNode;
@Child private InteropIsMemberReadableNode hasGroupNode;
@Child private InteropReadIntMemberNode readGroupNode;
private TRegexNamedCaptureGroupsAccessor() {
}
public static TRegexNamedCaptureGroupsAccessor create() {
return new TRegexNamedCaptureGroupsAccessor();
}
public boolean isNull(Object namedCaptureGroupsMap) {
return getIsNullNode().execute(namedCaptureGroupsMap);
}
public boolean hasGroup(Object namedCaptureGroupsMap, String name) {
return getHasGroupNode().execute(namedCaptureGroupsMap, name);
}
public int getGroupNumber(Object namedCaptureGroupsMap, String name) {
return getReadGroupNode().execute(namedCaptureGroupsMap, name);
}
private InteropIsNullNode getIsNullNode() {
if (isNullNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
isNullNode = insert(InteropIsNullNode.create());
}
return isNullNode;
}
private InteropIsMemberReadableNode getHasGroupNode() {
if (hasGroupNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
hasGroupNode = insert(InteropIsMemberReadableNode.create());
}
return hasGroupNode;
}
private InteropReadIntMemberNode getReadGroupNode() {
if (readGroupNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
readGroupNode = insert(InteropReadIntMemberNode.create());
}
return readGroupNode;
}
}
public static final class TRegexFlagsAccessor extends Node {
@Child private InteropReadStringMemberNode readSourceNode;
@Child private InteropReadBooleanMemberNode readGlobalNode;
@Child private InteropReadBooleanMemberNode readMultilineNode;
@Child private InteropReadBooleanMemberNode readIgnoreCaseNode;
@Child private InteropReadBooleanMemberNode readStickyNode;
@Child private InteropReadBooleanMemberNode readUnicodeNode;
@Child private InteropReadBooleanMemberNode readDotAllNode;
private TRegexFlagsAccessor() {
}
public static TRegexFlagsAccessor create() {
return new TRegexFlagsAccessor();
}
public String source(Object regexFlagsObject) {
return getReadSourceNode().execute(regexFlagsObject, Props.Flags.SOURCE);
}
public boolean global(Object regexFlagsObject) {
return getReadGlobalNode().execute(regexFlagsObject, Props.Flags.GLOBAL);
}
public boolean multiline(Object regexFlagsObject) {
return getReadMultilineNode().execute(regexFlagsObject, Props.Flags.MULTILINE);
}
public boolean ignoreCase(Object regexFlagsObject) {
return getReadIgnoreCaseNode().execute(regexFlagsObject, Props.Flags.IGNORE_CASE);
}
public boolean sticky(Object regexFlagsObject) {
return getReadStickyNode().execute(regexFlagsObject, Props.Flags.STICKY);
}
public boolean unicode(Object regexFlagsObject) {
return getReadUnicodeNode().execute(regexFlagsObject, Props.Flags.UNICODE);
}
public boolean dotAll(Object regexFlagsObject) {
return getReadDotAllNode().execute(regexFlagsObject, Props.Flags.DOT_ALL);
}
private InteropReadStringMemberNode getReadSourceNode() {
if (readSourceNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
readSourceNode = insert(InteropReadStringMemberNode.create());
}
return readSourceNode;
}
private InteropReadBooleanMemberNode getReadGlobalNode() {
if (readGlobalNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
readGlobalNode = insert(InteropReadBooleanMemberNode.create());
}
return readGlobalNode;
}
private InteropReadBooleanMemberNode getReadMultilineNode() {
if (readMultilineNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
readMultilineNode = insert(InteropReadBooleanMemberNode.create());
}
return readMultilineNode;
}
private InteropReadBooleanMemberNode getReadIgnoreCaseNode() {
if (readIgnoreCaseNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
readIgnoreCaseNode = insert(InteropReadBooleanMemberNode.create());
}
return readIgnoreCaseNode;
}
private InteropReadBooleanMemberNode getReadStickyNode() {
if (readStickyNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
readStickyNode = insert(InteropReadBooleanMemberNode.create());
}
return readStickyNode;
}
private InteropReadBooleanMemberNode getReadUnicodeNode() {
if (readUnicodeNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
readUnicodeNode = insert(InteropReadBooleanMemberNode.create());
}
return readUnicodeNode;
}
private InteropReadBooleanMemberNode getReadDotAllNode() {
if (readDotAllNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
readDotAllNode = insert(InteropReadBooleanMemberNode.create());
}
return readDotAllNode;
}
}
public static final class TRegexCompiledRegexSingleFlagAccessor extends Node {
private final String flag;
@Child private InteropReadMemberNode readFlagsObjectNode = InteropReadMemberNode.create();
@Child private InteropReadBooleanMemberNode readFlagNode = InteropReadBooleanMemberNode.create();
private TRegexCompiledRegexSingleFlagAccessor(String flag) {
this.flag = flag;
}
public static TRegexCompiledRegexSingleFlagAccessor create(String flag) {
return new TRegexCompiledRegexSingleFlagAccessor(flag);
}
public boolean get(Object compiledRegex) {
return readFlagNode.execute(readFlagsObjectNode.execute(compiledRegex, Props.CompiledRegex.FLAGS), flag);
}
}
public static final class TRegexResultAccessor extends Node {
private static final TRegexResultAccessor UNCACHED = new TRegexResultAccessor(false);
@Child private InteropReadBooleanMemberNode readIsMatchNode;
@Child private InvokeGetGroupBoundariesMethodNode getStartNode;
@Child private InvokeGetGroupBoundariesMethodNode getEndNode;
private TRegexResultAccessor(boolean cached) {
if (!cached) {
readIsMatchNode = InteropReadBooleanMemberNodeGen.getUncached();
getStartNode = InvokeGetGroupBoundariesMethodNodeGen.getUncached();
getEndNode = InvokeGetGroupBoundariesMethodNodeGen.getUncached();
}
}
public static TRegexResultAccessor create() {
return new TRegexResultAccessor(true);
}
public static TRegexResultAccessor getUncached() {
return UNCACHED;
}
public boolean isMatch(Object regexResultObject) {
return getReadIsMatchNode().execute(regexResultObject, Props.RegexResult.IS_MATCH);
}
public int captureGroupStart(Object regexResultObject, int i) {
return getGetStartNode().execute(regexResultObject, Props.RegexResult.GET_START, i);
}
public int captureGroupEnd(Object regexResultObject, int i) {
return getGetEndNode().execute(regexResultObject, Props.RegexResult.GET_END, i);
}
public int captureGroupLength(Object regexResultObject, int i) {
return captureGroupEnd(regexResultObject, i) - captureGroupStart(regexResultObject, i);
}
private InteropReadBooleanMemberNode getReadIsMatchNode() {
if (readIsMatchNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
readIsMatchNode = insert(InteropReadBooleanMemberNode.create());
}
return readIsMatchNode;
}
private InvokeGetGroupBoundariesMethodNode getGetStartNode() {
if (getStartNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
getStartNode = insert(InvokeGetGroupBoundariesMethodNode.create());
}
return getStartNode;
}
private InvokeGetGroupBoundariesMethodNode getGetEndNode() {
if (getEndNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
getEndNode = insert(InvokeGetGroupBoundariesMethodNode.create());
}
return getEndNode;
}
}
public static final class TRegexMaterializeResultNode extends Node {
private static final TRegexMaterializeResultNode UNCACHED = new TRegexMaterializeResultNode(false);
@Child TRegexResultAccessor resultAccessor;
private TRegexMaterializeResultNode(boolean cached) {
resultAccessor = cached ? TRegexResultAccessor.create() : TRegexResultAccessor.getUncached();
}
public static TRegexMaterializeResultNode create() {
return new TRegexMaterializeResultNode(true);
}
public static TRegexMaterializeResultNode getUncached() {
return UNCACHED;
}
public Object materializeGroup(Object regexResult, int i, String input) {
return materializeGroup(resultAccessor, regexResult, i, input);
}
public static Object materializeGroup(TRegexResultAccessor accessor, Object regexResult, int i, String input) {
final int beginIndex = accessor.captureGroupStart(regexResult, i);
if (beginIndex == Constants.CAPTURE_GROUP_NO_MATCH) {
assert i > 0;
return Undefined.instance;
} else {
return input.substring(beginIndex, accessor.captureGroupEnd(regexResult, i));
}
}
public Object[] materializeFull(Object regexResult, int groupCount, String input) {
Object[] result = new Object[groupCount];
for (int i = 0; i < groupCount; i++) {
result[i] = materializeGroup(regexResult, i, input);
}
return result;
}
}
}