package com.oracle.truffle.js.parser;
import java.io.PrintWriter;
import java.util.function.Function;
import com.oracle.js.parser.ErrorManager;
import com.oracle.js.parser.Lexer.LexerToken;
import com.oracle.js.parser.Lexer.RegexToken;
import com.oracle.js.parser.Parser;
import com.oracle.js.parser.ParserException;
import com.oracle.js.parser.ScriptEnvironment;
import com.oracle.js.parser.ScriptEnvironment.FunctionStatementBehavior;
import com.oracle.js.parser.Token;
import com.oracle.js.parser.TokenType;
import com.oracle.js.parser.ir.Expression;
import com.oracle.js.parser.ir.FunctionNode;
import com.oracle.js.parser.ir.Scope;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.js.parser.internal.ir.debug.JSONWriter;
import com.oracle.truffle.js.runtime.Errors;
import com.oracle.truffle.js.runtime.JSContext;
import com.oracle.truffle.js.runtime.JSException;
import com.oracle.truffle.js.runtime.JSParserOptions;
import com.oracle.truffle.js.runtime.JSRuntime;
import com.oracle.truffle.js.runtime.RegexCompilerInterface;
public final class GraalJSParserHelper {
private static final String NEVER_PART_OF_COMPILATION_MESSAGE = "do not parse from compiled code";
private GraalJSParserHelper() {
}
public static FunctionNode parseScript(JSContext context, com.oracle.truffle.api.source.Source truffleSource, JSParserOptions parserOptions) {
return parseScript(context, truffleSource, parserOptions, false, false, null, "", "");
}
public static FunctionNode parseScript(JSContext context, com.oracle.truffle.api.source.Source truffleSource, JSParserOptions parserOptions, boolean eval, boolean evalInFunction,
Scope evalScope, String prologue, String epilogue) {
return parseSource(context, truffleSource, parserOptions, false, eval, evalInFunction, evalScope, prologue, epilogue, null);
}
public static FunctionNode parseScript(JSContext context, com.oracle.truffle.api.source.Source truffleSource, JSParserOptions parserOptions, boolean eval, boolean evalInFunction,
Scope evalScope, String prologue, String epilogue, String[] argumentNames) {
return parseSource(context, truffleSource, parserOptions, false, eval, evalInFunction, evalScope, prologue, epilogue, argumentNames);
}
public static FunctionNode parseModule(JSContext context, com.oracle.truffle.api.source.Source truffleSource, JSParserOptions parserOptions) {
return parseSource(context, truffleSource, parserOptions, true, false, false, null, "", "", null);
}
private static FunctionNode parseSource(JSContext context, com.oracle.truffle.api.source.Source truffleSource, JSParserOptions parserOptions,
boolean parseModule, boolean eval, boolean evalInFunction, Scope evalScope, String prologue, String epilogue, String[] argumentNames) {
CompilerAsserts.neverPartOfCompilation(NEVER_PART_OF_COMPILATION_MESSAGE);
CharSequence code;
if (prologue.isEmpty() && epilogue.isEmpty()) {
code = truffleSource.getCharacters();
} else {
StringBuilder all = new StringBuilder();
all.append(prologue);
all.append(truffleSource.getCharacters());
all.append(epilogue);
code = all;
}
com.oracle.js.parser.Source source = com.oracle.js.parser.Source.sourceFor(truffleSource.getName(), code, eval);
ScriptEnvironment env = makeScriptEnvironment(parserOptions);
ErrorManager errors;
if (eval) {
errors = new ErrorManager.ThrowErrorManager();
} else {
errors = new ErrorManager.StringBuilderErrorManager();
}
errors.setLimit(0);
Parser parser = createParser(context, env, source, errors, parserOptions);
FunctionNode parsed;
if (parseModule) {
parsed = parser.parseModule(":module");
} else if (eval) {
parsed = parser.parseEval(evalInFunction, evalScope);
} else if (argumentNames != null) {
parsed = parser.parseWithArguments(argumentNames);
} else {
parsed = parser.parse();
}
if (errors.hasErrors()) {
throwErrors(truffleSource, errors);
}
return parsed;
}
public static Expression parseExpression(JSContext context, com.oracle.truffle.api.source.Source truffleSource, JSParserOptions parserOptions) {
CompilerAsserts.neverPartOfCompilation(NEVER_PART_OF_COMPILATION_MESSAGE);
CharSequence code = truffleSource.getCharacters();
com.oracle.js.parser.Source source = com.oracle.js.parser.Source.sourceFor(truffleSource.getName(), code, true);
ScriptEnvironment env = makeScriptEnvironment(parserOptions);
ErrorManager errors = new ErrorManager.ThrowErrorManager();
errors.setLimit(0);
Parser parser = createParser(context, env, source, errors, parserOptions);
Expression expression = parser.parseExpression();
if (errors.hasErrors()) {
throwErrors(truffleSource, errors);
}
return expression;
}
private static Parser createParser(JSContext context, ScriptEnvironment env, com.oracle.js.parser.Source source, ErrorManager errors, JSParserOptions parserOptions) {
return new Parser(env, source, errors) {
@Override
protected void validateLexerToken(LexerToken lexerToken) {
if (lexerToken instanceof RegexToken) {
final RegexToken regex = (RegexToken) lexerToken;
if (context.getContextOptions().isValidateRegExpLiterals()) {
try {
RegexCompilerInterface.validate(context, regex.getExpression(), regex.getOptions(), parserOptions.getEcmaScriptVersion());
} catch (JSException e) {
throw error(e.getRawMessage());
}
}
}
}
@Override
protected Function<Number, String> getNumberToStringConverter() {
return JSRuntime::numberToString;
}
};
}
private static ScriptEnvironment makeScriptEnvironment(JSParserOptions parserOptions) {
ScriptEnvironment.Builder builder = ScriptEnvironment.builder();
builder.strict(parserOptions.isStrict());
builder.ecmaScriptVersion(parserOptions.getEcmaScriptVersion());
builder.emptyStatements(parserOptions.isEmptyStatements());
builder.syntaxExtensions(parserOptions.isSyntaxExtensions());
builder.scripting(parserOptions.isScripting());
builder.shebang(parserOptions.isShebang());
builder.constAsVar(parserOptions.isConstAsVar());
builder.allowBigInt(parserOptions.isAllowBigInt());
builder.annexB(parserOptions.isAnnexB());
builder.classFields(parserOptions.isClassFields());
if (parserOptions.isFunctionStatementError()) {
builder.functionStatementBehavior(FunctionStatementBehavior.ERROR);
} else {
builder.functionStatementBehavior(FunctionStatementBehavior.ACCEPT);
}
if (parserOptions.isDumpOnError()) {
builder.dumpOnError(new PrintWriter(System.err, true));
}
return builder.build();
}
public static void checkFunctionSyntax(JSContext context, JSParserOptions parserOptions, String parameterList, String body, boolean generator, boolean async, String sourceName) {
CompilerAsserts.neverPartOfCompilation(NEVER_PART_OF_COMPILATION_MESSAGE);
ScriptEnvironment env = makeScriptEnvironment(parserOptions);
ErrorManager errors = new com.oracle.js.parser.ErrorManager.ThrowErrorManager();
Parser parser = createParser(context, env, com.oracle.js.parser.Source.sourceFor(sourceName, parameterList), errors, parserOptions);
parser.parseFormalParameterList();
parser = createParser(context, env, com.oracle.js.parser.Source.sourceFor(sourceName, body), errors, parserOptions);
parser.parseFunctionBody(generator, async);
}
private static void throwErrors(com.oracle.truffle.api.source.Source source, ErrorManager errors) {
ParserException parserException = errors.getParserException();
SourceSection sourceLocation = null;
boolean isIncompleteSource = false;
if (parserException != null) {
isIncompleteSource = parserException.isIncompleteSource();
if (parserException.getPosition() >= 0) {
int length = Token.descType(parserException.getToken()) == TokenType.EOL ? 0 : Token.descLength(parserException.getToken());
sourceLocation = source.createSection(parserException.getPosition(), length);
}
if (parserException.getErrorType() == com.oracle.js.parser.JSErrorType.ReferenceError) {
throw Errors.createReferenceError(parserException.getMessage(), sourceLocation);
} else {
assert parserException.getErrorType() == com.oracle.js.parser.JSErrorType.SyntaxError;
}
}
throw Errors.createSyntaxError(((ErrorManager.StringBuilderErrorManager) errors).getOutput(), sourceLocation, isIncompleteSource);
}
public static String parseToJSON(String code, String name, boolean includeLoc, JSParserOptions parserOptions) {
CompilerAsserts.neverPartOfCompilation(NEVER_PART_OF_COMPILATION_MESSAGE);
ScriptEnvironment env = makeScriptEnvironment(parserOptions);
try {
return JSONWriter.parse(env, code, name, includeLoc);
} catch (ParserException e) {
throw Errors.createSyntaxError(e.getMessage());
}
}
}