Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 65 additions & 32 deletions src/parser/plugins/validate-keyword.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,56 @@ import { AiScriptSyntaxError } from '../../error.js';
import { visitNode } from '../visit.js';
import type * as Ast from '../../node.js';

const reservedWord = [
'null',
'true',
'false',
'each',
'for',
'loop',
'break',
'continue',
'match',
'if',
'elif',
'else',
'return',
'eval',
'var',
'let',
'exists',
// 予約語となっている識別子があるかを確認する。
// - キーワードは字句解析の段階でそれぞれのKeywordトークンとなるため除外
// - 文脈キーワードは識別子に利用できるため除外

// future
'fn',
'namespace',
'meta',
const reservedWord = [
'as',
'async',
'attr',
'attribute',
'static',
'await',
'catch',
'class',
'struct',
'module',
'while',
'import',
'export',
// 'const',
'component',
'constructor',
// 'def',
'dictionary',
'do',
'enum',
'export',
'finally',
'fn',
// 'func',
// 'function',
// 'ref',
// 'out',
'hash',
'in',
'interface',
'out',
'private',
'public',
'ref',
'static',
'struct',
'table',
'this',
'throw',
'trait',
'try',
'undefined',
'use',
'using',
'when',
'while',
'yield',
'import',
'is',
'meta',
'module',
'namespace',
'new',
];

function throwReservedWordError(name: string, loc: Ast.Loc): void {
Expand All @@ -48,10 +60,11 @@ function throwReservedWordError(name: string, loc: Ast.Loc): void {

function validateNode(node: Ast.Node): Ast.Node {
switch (node.type) {
case 'ns':
case 'def':
case 'attr':
case 'ns':
case 'identifier': {
case 'identifier':
case 'prop': {
if (reservedWord.includes(node.name)) {
throwReservedWordError(node.name, node.loc);
}
Expand All @@ -63,6 +76,18 @@ function validateNode(node: Ast.Node): Ast.Node {
}
break;
}
case 'each': {
if (reservedWord.includes(node.var)) {
throwReservedWordError(node.var, node.loc);
}
break;
}
case 'for': {
if (node.var != null && reservedWord.includes(node.var)) {
throwReservedWordError(node.var, node.loc);
}
break;
}
case 'fn': {
for (const arg of node.args) {
if (reservedWord.includes(arg.name)) {
Expand All @@ -71,6 +96,14 @@ function validateNode(node: Ast.Node): Ast.Node {
}
break;
}
case 'obj': {
for (const name of node.value.keys()) {
if (reservedWord.includes(name)) {
throwReservedWordError(name, node.loc);
}
}
break;
}
}

return node;
Expand Down
12 changes: 3 additions & 9 deletions test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import * as assert from 'assert';
import { Parser, Interpreter, utils, errors, Ast } from '../src';
import { NUM, STR, NULL, ARR, OBJ, BOOL, TRUE, FALSE, ERROR ,FN_NATIVE } from '../src/interpreter/value';
import { AiScriptSyntaxError } from '../src/error';
let { AiScriptRuntimeError, AiScriptIndexOutOfRangeError } = errors;

const exe = (program: string): Promise<any> => new Promise((ok, err) => {
Expand Down Expand Up @@ -2886,7 +2887,7 @@ describe('Security', () => {
`);
assert.fail();
} catch (e) {
assert.ok(e instanceof AiScriptRuntimeError);
assert.ok(e instanceof AiScriptSyntaxError);
}

try {
Expand All @@ -2909,13 +2910,6 @@ describe('Security', () => {
});

test.concurrent('Cannot access js native property via object', async () => {
const res1 = await exe(`
let obj = {}

<: obj.constructor
`);
eq(res1, NULL);

const res2 = await exe(`
let obj = {}

Expand All @@ -2938,7 +2932,7 @@ describe('Security', () => {
`);
assert.fail();
} catch (e) {
assert.ok(e instanceof AiScriptRuntimeError);
assert.ok(e instanceof AiScriptSyntaxError);
}

try {
Expand Down