Skip to content

Commit 3c26949

Browse files
Add annexb: false parser option to disable Annex B (#15320)
1 parent 1004037 commit 3c26949

49 files changed

Lines changed: 1335 additions & 27 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/babel-parser/src/options.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export type Options = {
2222
createParenthesizedExpressions: boolean;
2323
errorRecovery: boolean;
2424
attachComment: boolean;
25+
annexB: boolean;
2526
};
2627

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

7983
// Interpret and default an options object
8084

8185
export function getOptions(opts?: Options | null): Options {
86+
if (opts && opts.annexB != null && opts.annexB !== false) {
87+
throw new Error("The `annexB` option can only be set to `false`.");
88+
}
89+
8290
const options: any = {};
8391
for (const key of Object.keys(defaultOptions)) {
8492
// @ts-expect-error key may not exist in opts

packages/babel-parser/src/parse-error/standard-errors.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@ export default {
226226
RecordNoProto: "'__proto__' is not allowed in Record expressions.",
227227
RestTrailingComma: "Unexpected trailing comma after rest element.",
228228
SloppyFunction:
229+
"In non-strict mode code, functions can only be declared at top level or inside a block.",
230+
SloppyFunctionAnnexB:
229231
"In non-strict mode code, functions can only be declared at top level, inside a block, or as the body of an if statement.",
230232
StaticPrototype: "Classes may not have static property named prototype.",
231233
SuperNotAllowed:

packages/babel-parser/src/parser/statement.ts

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,8 @@ export default abstract class StatementParser extends ExpressionParser {
354354
ParseStatementFlag.AllowImportExport |
355355
ParseStatementFlag.AllowDeclaration |
356356
ParseStatementFlag.AllowFunctionDeclaration |
357+
// This function is actually also used to parse StatementItems,
358+
// which with Annex B enabled allows labeled functions.
357359
ParseStatementFlag.AllowLabeledFunction,
358360
);
359361
}
@@ -363,18 +365,24 @@ export default abstract class StatementParser extends ExpressionParser {
363365
return this.parseStatementLike(
364366
ParseStatementFlag.AllowDeclaration |
365367
ParseStatementFlag.AllowFunctionDeclaration |
366-
ParseStatementFlag.AllowLabeledFunction,
368+
(!this.options.annexB || this.state.strict
369+
? 0
370+
: ParseStatementFlag.AllowLabeledFunction),
367371
);
368372
}
369373

370-
parseStatementOrFunctionDeclaration(
374+
parseStatementOrSloppyAnnexBFunctionDeclaration(
371375
this: Parser,
372-
disallowLabeledFunction: boolean,
376+
allowLabeledFunction: boolean = false,
373377
) {
374-
return this.parseStatementLike(
375-
ParseStatementFlag.AllowFunctionDeclaration |
376-
(disallowLabeledFunction ? 0 : ParseStatementFlag.AllowLabeledFunction),
377-
);
378+
let flags: ParseStatementFlag = ParseStatementFlag.StatementOnly;
379+
if (this.options.annexB && !this.state.strict) {
380+
flags |= ParseStatementFlag.AllowFunctionDeclaration;
381+
if (allowLabeledFunction) {
382+
flags |= ParseStatementFlag.AllowLabeledFunction;
383+
}
384+
}
385+
return this.parseStatementLike(flags);
378386
}
379387

380388
// Parse a single statement.
@@ -438,12 +446,15 @@ export default abstract class StatementParser extends ExpressionParser {
438446
return this.parseForStatement(node as Undone<N.ForStatement>);
439447
case tt._function:
440448
if (this.lookaheadCharCode() === charCodes.dot) break;
441-
if (!allowDeclaration) {
442-
if (this.state.strict) {
443-
this.raise(Errors.StrictFunction, { at: this.state.startLoc });
444-
} else if (!allowFunctionDeclaration) {
445-
this.raise(Errors.SloppyFunction, { at: this.state.startLoc });
446-
}
449+
if (!allowFunctionDeclaration) {
450+
this.raise(
451+
this.state.strict
452+
? Errors.StrictFunction
453+
: this.options.annexB
454+
? Errors.SloppyFunctionAnnexB
455+
: Errors.SloppyFunction,
456+
{ at: this.state.startLoc },
457+
);
447458
}
448459
return this.parseFunctionStatement(
449460
node as Undone<N.FunctionDeclaration>,
@@ -981,12 +992,9 @@ export default abstract class StatementParser extends ExpressionParser {
981992
node.test = this.parseHeaderExpression();
982993
// Annex B.3.3
983994
// https://tc39.es/ecma262/#sec-functiondeclarations-in-ifstatement-statement-clauses
984-
node.consequent = this.parseStatementOrFunctionDeclaration(
985-
// https://tc39.es/ecma262/#sec-if-statement-static-semantics-early-errors
986-
true,
987-
);
995+
node.consequent = this.parseStatementOrSloppyAnnexBFunctionDeclaration();
988996
node.alternate = this.eat(tt._else)
989-
? this.parseStatementOrFunctionDeclaration(true)
997+
? this.parseStatementOrSloppyAnnexBFunctionDeclaration()
990998
: null;
991999
return this.finishNode(node, "IfStatement");
9921000
}
@@ -1074,8 +1082,11 @@ export default abstract class StatementParser extends ExpressionParser {
10741082
parseCatchClauseParam(this: Parser): N.Pattern {
10751083
const param = this.parseBindingAtom();
10761084

1077-
const simple = param.type === "Identifier";
1078-
this.scope.enter(simple ? SCOPE_SIMPLE_CATCH : 0);
1085+
this.scope.enter(
1086+
this.options.annexB && param.type === "Identifier"
1087+
? SCOPE_SIMPLE_CATCH
1088+
: 0,
1089+
);
10791090
this.checkLVal(param, {
10801091
in: { type: "CatchClause" },
10811092
binding: BIND_CATCH_PARAM,
@@ -1234,7 +1245,7 @@ export default abstract class StatementParser extends ExpressionParser {
12341245
// https://tc39.es/ecma262/#prod-LabelledItem
12351246
node.body =
12361247
flags & ParseStatementFlag.AllowLabeledFunction
1237-
? this.parseStatementOrFunctionDeclaration(false)
1248+
? this.parseStatementOrSloppyAnnexBFunctionDeclaration(true)
12381249
: this.parseStatement();
12391250

12401251
this.state.labels.pop();
@@ -1419,6 +1430,7 @@ export default abstract class StatementParser extends ExpressionParser {
14191430
init.type === "VariableDeclaration" &&
14201431
init.declarations[0].init != null &&
14211432
(!isForIn ||
1433+
!this.options.annexB ||
14221434
this.state.strict ||
14231435
init.kind !== "var" ||
14241436
init.declarations[0].id.type !== "Identifier")
@@ -1626,7 +1638,7 @@ export default abstract class StatementParser extends ExpressionParser {
16261638
// treatFunctionsAsVar).
16271639
this.scope.declareName(
16281640
node.id.name,
1629-
this.state.strict || node.generator || node.async
1641+
!this.options.annexB || this.state.strict || node.generator || node.async
16301642
? this.scope.treatFunctionsAsVar
16311643
? BIND_VAR
16321644
: BIND_LEXICAL

packages/babel-parser/src/plugins/placeholders.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ export default (superClass: typeof Parser) =>
194194
const stmt: N.LabeledStatement = node;
195195
stmt.label = this.finishPlaceholder(expr, "Identifier");
196196
this.next();
197-
stmt.body = super.parseStatementOrFunctionDeclaration(false);
197+
stmt.body = super.parseStatementOrSloppyAnnexBFunctionDeclaration();
198198
return this.finishNode(stmt, "LabeledStatement");
199199
}
200200

packages/babel-parser/src/tokenizer/index.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,11 @@ export default abstract class Tokenizer extends CommentsParser {
373373
default:
374374
if (isWhitespace(ch)) {
375375
++this.state.pos;
376-
} else if (ch === charCodes.dash && !this.inModule) {
376+
} else if (
377+
ch === charCodes.dash &&
378+
!this.inModule &&
379+
this.options.annexB
380+
) {
377381
const pos = this.state.pos;
378382
if (
379383
this.input.charCodeAt(pos + 1) === charCodes.dash &&
@@ -389,7 +393,11 @@ export default abstract class Tokenizer extends CommentsParser {
389393
} else {
390394
break loop;
391395
}
392-
} else if (ch === charCodes.lessThan && !this.inModule) {
396+
} else if (
397+
ch === charCodes.lessThan &&
398+
!this.inModule &&
399+
this.options.annexB
400+
) {
393401
const pos = this.state.pos;
394402
if (
395403
this.input.charCodeAt(pos + 1) === charCodes.exclamationMark &&

packages/babel-parser/src/util/scope.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ export default class ScopeHandler<IScope extends Scope = Scope> {
184184

185185
return (
186186
(scope.lexical.has(name) &&
187+
// Annex B.3.4
188+
// https://tc39.es/ecma262/#sec-variablestatements-in-catch-blocks
187189
!(
188190
scope.flags & SCOPE_SIMPLE_CATCH &&
189191
scope.lexical.values().next().value === name
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-->b;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"throws": "Unexpected token (1:2)"
3+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
a<!--b;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"type": "File",
3+
"start":0,"end":7,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":7,"index":7}},
4+
"program": {
5+
"type": "Program",
6+
"start":0,"end":7,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":7,"index":7}},
7+
"sourceType": "script",
8+
"interpreter": null,
9+
"body": [
10+
{
11+
"type": "ExpressionStatement",
12+
"start":0,"end":7,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":7,"index":7}},
13+
"expression": {
14+
"type": "BinaryExpression",
15+
"start":0,"end":6,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":6,"index":6}},
16+
"left": {
17+
"type": "Identifier",
18+
"start":0,"end":1,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":1,"index":1},"identifierName":"a"},
19+
"name": "a"
20+
},
21+
"operator": "<",
22+
"right": {
23+
"type": "UnaryExpression",
24+
"start":2,"end":6,"loc":{"start":{"line":1,"column":2,"index":2},"end":{"line":1,"column":6,"index":6}},
25+
"operator": "!",
26+
"prefix": true,
27+
"argument": {
28+
"type": "UpdateExpression",
29+
"start":3,"end":6,"loc":{"start":{"line":1,"column":3,"index":3},"end":{"line":1,"column":6,"index":6}},
30+
"operator": "--",
31+
"prefix": true,
32+
"argument": {
33+
"type": "Identifier",
34+
"start":5,"end":6,"loc":{"start":{"line":1,"column":5,"index":5},"end":{"line":1,"column":6,"index":6},"identifierName":"b"},
35+
"name": "b"
36+
}
37+
}
38+
}
39+
}
40+
}
41+
],
42+
"directives": []
43+
}
44+
}

0 commit comments

Comments
 (0)