Skip to content

Commit

Permalink
Add annexb: false parser option to disable Annex B (#15320)
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolo-ribaudo committed Feb 18, 2023
1 parent 1004037 commit 3c26949
Show file tree
Hide file tree
Showing 49 changed files with 1,335 additions and 27 deletions.
8 changes: 8 additions & 0 deletions packages/babel-parser/src/options.ts
Expand Up @@ -22,6 +22,7 @@ export type Options = {
createParenthesizedExpressions: boolean;
errorRecovery: boolean;
attachComment: boolean;
annexB: boolean;
};

export const defaultOptions: Options = {
Expand Down Expand Up @@ -74,11 +75,18 @@ export const defaultOptions: Options = {
// is vital to preserve comments after transform. If you don't print AST back,
// consider set this option to `false` for performance
attachComment: true,
// When enabled, the parser will support Annex B syntax.
// https://tc39.es/ecma262/#sec-additional-ecmascript-features-for-web-browsers
annexB: true,
};

// Interpret and default an options object

export function getOptions(opts?: Options | null): Options {
if (opts && opts.annexB != null && opts.annexB !== false) {
throw new Error("The `annexB` option can only be set to `false`.");
}

const options: any = {};
for (const key of Object.keys(defaultOptions)) {
// @ts-expect-error key may not exist in opts
Expand Down
2 changes: 2 additions & 0 deletions packages/babel-parser/src/parse-error/standard-errors.ts
Expand Up @@ -226,6 +226,8 @@ export default {
RecordNoProto: "'__proto__' is not allowed in Record expressions.",
RestTrailingComma: "Unexpected trailing comma after rest element.",
SloppyFunction:
"In non-strict mode code, functions can only be declared at top level or inside a block.",
SloppyFunctionAnnexB:
"In non-strict mode code, functions can only be declared at top level, inside a block, or as the body of an if statement.",
StaticPrototype: "Classes may not have static property named prototype.",
SuperNotAllowed:
Expand Down
56 changes: 34 additions & 22 deletions packages/babel-parser/src/parser/statement.ts
Expand Up @@ -354,6 +354,8 @@ export default abstract class StatementParser extends ExpressionParser {
ParseStatementFlag.AllowImportExport |
ParseStatementFlag.AllowDeclaration |
ParseStatementFlag.AllowFunctionDeclaration |
// This function is actually also used to parse StatementItems,
// which with Annex B enabled allows labeled functions.
ParseStatementFlag.AllowLabeledFunction,
);
}
Expand All @@ -363,18 +365,24 @@ export default abstract class StatementParser extends ExpressionParser {
return this.parseStatementLike(
ParseStatementFlag.AllowDeclaration |
ParseStatementFlag.AllowFunctionDeclaration |
ParseStatementFlag.AllowLabeledFunction,
(!this.options.annexB || this.state.strict
? 0
: ParseStatementFlag.AllowLabeledFunction),
);
}

parseStatementOrFunctionDeclaration(
parseStatementOrSloppyAnnexBFunctionDeclaration(
this: Parser,
disallowLabeledFunction: boolean,
allowLabeledFunction: boolean = false,
) {
return this.parseStatementLike(
ParseStatementFlag.AllowFunctionDeclaration |
(disallowLabeledFunction ? 0 : ParseStatementFlag.AllowLabeledFunction),
);
let flags: ParseStatementFlag = ParseStatementFlag.StatementOnly;
if (this.options.annexB && !this.state.strict) {
flags |= ParseStatementFlag.AllowFunctionDeclaration;
if (allowLabeledFunction) {
flags |= ParseStatementFlag.AllowLabeledFunction;
}
}
return this.parseStatementLike(flags);
}

// Parse a single statement.
Expand Down Expand Up @@ -438,12 +446,15 @@ export default abstract class StatementParser extends ExpressionParser {
return this.parseForStatement(node as Undone<N.ForStatement>);
case tt._function:
if (this.lookaheadCharCode() === charCodes.dot) break;
if (!allowDeclaration) {
if (this.state.strict) {
this.raise(Errors.StrictFunction, { at: this.state.startLoc });
} else if (!allowFunctionDeclaration) {
this.raise(Errors.SloppyFunction, { at: this.state.startLoc });
}
if (!allowFunctionDeclaration) {
this.raise(
this.state.strict
? Errors.StrictFunction
: this.options.annexB
? Errors.SloppyFunctionAnnexB
: Errors.SloppyFunction,
{ at: this.state.startLoc },
);
}
return this.parseFunctionStatement(
node as Undone<N.FunctionDeclaration>,
Expand Down Expand Up @@ -981,12 +992,9 @@ export default abstract class StatementParser extends ExpressionParser {
node.test = this.parseHeaderExpression();
// Annex B.3.3
// https://tc39.es/ecma262/#sec-functiondeclarations-in-ifstatement-statement-clauses
node.consequent = this.parseStatementOrFunctionDeclaration(
// https://tc39.es/ecma262/#sec-if-statement-static-semantics-early-errors
true,
);
node.consequent = this.parseStatementOrSloppyAnnexBFunctionDeclaration();
node.alternate = this.eat(tt._else)
? this.parseStatementOrFunctionDeclaration(true)
? this.parseStatementOrSloppyAnnexBFunctionDeclaration()
: null;
return this.finishNode(node, "IfStatement");
}
Expand Down Expand Up @@ -1074,8 +1082,11 @@ export default abstract class StatementParser extends ExpressionParser {
parseCatchClauseParam(this: Parser): N.Pattern {
const param = this.parseBindingAtom();

const simple = param.type === "Identifier";
this.scope.enter(simple ? SCOPE_SIMPLE_CATCH : 0);
this.scope.enter(
this.options.annexB && param.type === "Identifier"
? SCOPE_SIMPLE_CATCH
: 0,
);
this.checkLVal(param, {
in: { type: "CatchClause" },
binding: BIND_CATCH_PARAM,
Expand Down Expand Up @@ -1234,7 +1245,7 @@ export default abstract class StatementParser extends ExpressionParser {
// https://tc39.es/ecma262/#prod-LabelledItem
node.body =
flags & ParseStatementFlag.AllowLabeledFunction
? this.parseStatementOrFunctionDeclaration(false)
? this.parseStatementOrSloppyAnnexBFunctionDeclaration(true)
: this.parseStatement();

this.state.labels.pop();
Expand Down Expand Up @@ -1419,6 +1430,7 @@ export default abstract class StatementParser extends ExpressionParser {
init.type === "VariableDeclaration" &&
init.declarations[0].init != null &&
(!isForIn ||
!this.options.annexB ||
this.state.strict ||
init.kind !== "var" ||
init.declarations[0].id.type !== "Identifier")
Expand Down Expand Up @@ -1626,7 +1638,7 @@ export default abstract class StatementParser extends ExpressionParser {
// treatFunctionsAsVar).
this.scope.declareName(
node.id.name,
this.state.strict || node.generator || node.async
!this.options.annexB || this.state.strict || node.generator || node.async
? this.scope.treatFunctionsAsVar
? BIND_VAR
: BIND_LEXICAL
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-parser/src/plugins/placeholders.ts
Expand Up @@ -194,7 +194,7 @@ export default (superClass: typeof Parser) =>
const stmt: N.LabeledStatement = node;
stmt.label = this.finishPlaceholder(expr, "Identifier");
this.next();
stmt.body = super.parseStatementOrFunctionDeclaration(false);
stmt.body = super.parseStatementOrSloppyAnnexBFunctionDeclaration();
return this.finishNode(stmt, "LabeledStatement");
}

Expand Down
12 changes: 10 additions & 2 deletions packages/babel-parser/src/tokenizer/index.ts
Expand Up @@ -373,7 +373,11 @@ export default abstract class Tokenizer extends CommentsParser {
default:
if (isWhitespace(ch)) {
++this.state.pos;
} else if (ch === charCodes.dash && !this.inModule) {
} else if (
ch === charCodes.dash &&
!this.inModule &&
this.options.annexB
) {
const pos = this.state.pos;
if (
this.input.charCodeAt(pos + 1) === charCodes.dash &&
Expand All @@ -389,7 +393,11 @@ export default abstract class Tokenizer extends CommentsParser {
} else {
break loop;
}
} else if (ch === charCodes.lessThan && !this.inModule) {
} else if (
ch === charCodes.lessThan &&
!this.inModule &&
this.options.annexB
) {
const pos = this.state.pos;
if (
this.input.charCodeAt(pos + 1) === charCodes.exclamationMark &&
Expand Down
2 changes: 2 additions & 0 deletions packages/babel-parser/src/util/scope.ts
Expand Up @@ -184,6 +184,8 @@ export default class ScopeHandler<IScope extends Scope = Scope> {

return (
(scope.lexical.has(name) &&
// Annex B.3.4
// https://tc39.es/ecma262/#sec-variablestatements-in-catch-blocks
!(
scope.flags & SCOPE_SIMPLE_CATCH &&
scope.lexical.values().next().value === name
Expand Down
@@ -0,0 +1 @@
-->b;
@@ -0,0 +1,3 @@
{
"throws": "Unexpected token (1:2)"
}
@@ -0,0 +1 @@
a<!--b;
@@ -0,0 +1,44 @@
{
"type": "File",
"start":0,"end":7,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":7,"index":7}},
"program": {
"type": "Program",
"start":0,"end":7,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":7,"index":7}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ExpressionStatement",
"start":0,"end":7,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":7,"index":7}},
"expression": {
"type": "BinaryExpression",
"start":0,"end":6,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":6,"index":6}},
"left": {
"type": "Identifier",
"start":0,"end":1,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":1,"index":1},"identifierName":"a"},
"name": "a"
},
"operator": "<",
"right": {
"type": "UnaryExpression",
"start":2,"end":6,"loc":{"start":{"line":1,"column":2,"index":2},"end":{"line":1,"column":6,"index":6}},
"operator": "!",
"prefix": true,
"argument": {
"type": "UpdateExpression",
"start":3,"end":6,"loc":{"start":{"line":1,"column":3,"index":3},"end":{"line":1,"column":6,"index":6}},
"operator": "--",
"prefix": true,
"argument": {
"type": "Identifier",
"start":5,"end":6,"loc":{"start":{"line":1,"column":5,"index":5},"end":{"line":1,"column":6,"index":6},"identifierName":"b"},
"name": "b"
}
}
}
}
}
],
"directives": []
}
}
@@ -0,0 +1 @@
if (0) x: function f() {}
@@ -0,0 +1,57 @@
{
"type": "File",
"start":0,"end":25,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":25,"index":25}},
"errors": [
"SyntaxError: In non-strict mode code, functions can only be declared at top level or inside a block. (1:10)"
],
"program": {
"type": "Program",
"start":0,"end":25,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":25,"index":25}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "IfStatement",
"start":0,"end":25,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":25,"index":25}},
"test": {
"type": "NumericLiteral",
"start":4,"end":5,"loc":{"start":{"line":1,"column":4,"index":4},"end":{"line":1,"column":5,"index":5}},
"extra": {
"rawValue": 0,
"raw": "0"
},
"value": 0
},
"consequent": {
"type": "LabeledStatement",
"start":7,"end":25,"loc":{"start":{"line":1,"column":7,"index":7},"end":{"line":1,"column":25,"index":25}},
"body": {
"type": "FunctionDeclaration",
"start":10,"end":25,"loc":{"start":{"line":1,"column":10,"index":10},"end":{"line":1,"column":25,"index":25}},
"id": {
"type": "Identifier",
"start":19,"end":20,"loc":{"start":{"line":1,"column":19,"index":19},"end":{"line":1,"column":20,"index":20},"identifierName":"f"},
"name": "f"
},
"generator": false,
"async": false,
"params": [],
"body": {
"type": "BlockStatement",
"start":23,"end":25,"loc":{"start":{"line":1,"column":23,"index":23},"end":{"line":1,"column":25,"index":25}},
"body": [],
"directives": []
}
},
"label": {
"type": "Identifier",
"start":7,"end":8,"loc":{"start":{"line":1,"column":7,"index":7},"end":{"line":1,"column":8,"index":8},"identifierName":"x"},
"name": "x"
}
},
"alternate": null
}
],
"directives": []
}
}
@@ -0,0 +1 @@
x: y: function fn() {}
@@ -0,0 +1,52 @@
{
"type": "File",
"start":0,"end":22,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":22,"index":22}},
"errors": [
"SyntaxError: In non-strict mode code, functions can only be declared at top level or inside a block. (1:6)"
],
"program": {
"type": "Program",
"start":0,"end":22,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":22,"index":22}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "LabeledStatement",
"start":0,"end":22,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":22,"index":22}},
"body": {
"type": "LabeledStatement",
"start":3,"end":22,"loc":{"start":{"line":1,"column":3,"index":3},"end":{"line":1,"column":22,"index":22}},
"body": {
"type": "FunctionDeclaration",
"start":6,"end":22,"loc":{"start":{"line":1,"column":6,"index":6},"end":{"line":1,"column":22,"index":22}},
"id": {
"type": "Identifier",
"start":15,"end":17,"loc":{"start":{"line":1,"column":15,"index":15},"end":{"line":1,"column":17,"index":17},"identifierName":"fn"},
"name": "fn"
},
"generator": false,
"async": false,
"params": [],
"body": {
"type": "BlockStatement",
"start":20,"end":22,"loc":{"start":{"line":1,"column":20,"index":20},"end":{"line":1,"column":22,"index":22}},
"body": [],
"directives": []
}
},
"label": {
"type": "Identifier",
"start":3,"end":4,"loc":{"start":{"line":1,"column":3,"index":3},"end":{"line":1,"column":4,"index":4},"identifierName":"y"},
"name": "y"
}
},
"label": {
"type": "Identifier",
"start":0,"end":1,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":1,"index":1},"identifierName":"x"},
"name": "x"
}
}
],
"directives": []
}
}
@@ -0,0 +1 @@
x: function fn() {}

0 comments on commit 3c26949

Please sign in to comment.