Skip to content

(RFC) utils/rttc.ts: Add integer check for array indices #282

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 10, 2019
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
65 changes: 49 additions & 16 deletions src/utils/__tests__/__snapshots__/rttc.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -8317,8 +8317,8 @@ Object {

exports[`Member expression type combinations: Invalid type combinations return TypeError 54`] = `
Object {
"elaborate": "Expected number as prop, got boolean.",
"explain": "Expected number as prop, got boolean.",
"elaborate": "Expected array index as prop, got boolean.",
"explain": "Expected array index as prop, got boolean.",
"left": Array [
2,
],
Expand All @@ -8328,8 +8328,8 @@ Object {

exports[`Member expression type combinations: Invalid type combinations return TypeError 55`] = `
Object {
"elaborate": "Expected number as prop, got string.",
"explain": "Expected number as prop, got string.",
"elaborate": "Expected array index as prop, got string.",
"explain": "Expected array index as prop, got string.",
"left": Array [
2,
],
Expand All @@ -8339,8 +8339,8 @@ Object {

exports[`Member expression type combinations: Invalid type combinations return TypeError 56`] = `
Object {
"elaborate": "Expected number as prop, got function.",
"explain": "Expected number as prop, got function.",
"elaborate": "Expected array index as prop, got function.",
"explain": "Expected array index as prop, got function.",
"left": Array [
2,
],
Expand All @@ -8350,8 +8350,8 @@ Object {

exports[`Member expression type combinations: Invalid type combinations return TypeError 57`] = `
Object {
"elaborate": "Expected number as prop, got function.",
"explain": "Expected number as prop, got function.",
"elaborate": "Expected array index as prop, got function.",
"explain": "Expected array index as prop, got function.",
"left": Array [
2,
],
Expand All @@ -8361,8 +8361,8 @@ Object {

exports[`Member expression type combinations: Invalid type combinations return TypeError 58`] = `
Object {
"elaborate": "Expected number as prop, got object.",
"explain": "Expected number as prop, got object.",
"elaborate": "Expected array index as prop, got object.",
"explain": "Expected array index as prop, got object.",
"left": Array [
2,
],
Expand All @@ -8374,8 +8374,8 @@ Object {

exports[`Member expression type combinations: Invalid type combinations return TypeError 59`] = `
Object {
"elaborate": "Expected number as prop, got array.",
"explain": "Expected number as prop, got array.",
"elaborate": "Expected array index as prop, got array.",
"explain": "Expected array index as prop, got array.",
"left": Array [
2,
],
Expand All @@ -8387,8 +8387,8 @@ Object {

exports[`Member expression type combinations: Invalid type combinations return TypeError 60`] = `
Object {
"elaborate": "Expected number as prop, got undefined.",
"explain": "Expected number as prop, got undefined.",
"elaborate": "Expected array index as prop, got undefined.",
"explain": "Expected array index as prop, got undefined.",
"left": Array [
2,
],
Expand All @@ -8398,8 +8398,8 @@ Object {

exports[`Member expression type combinations: Invalid type combinations return TypeError 61`] = `
Object {
"elaborate": "Expected number as prop, got null.",
"explain": "Expected number as prop, got null.",
"elaborate": "Expected array index as prop, got null.",
"explain": "Expected array index as prop, got null.",
"left": Array [
2,
],
Expand Down Expand Up @@ -8577,6 +8577,39 @@ Object {
}
`;

exports[`Member expression type combinations: Invalid type combinations return TypeError 80`] = `
Object {
"elaborate": "Expected array index as prop, got other number.",
"explain": "Expected array index as prop, got other number.",
"left": Array [
2,
],
"right": -1,
}
`;

exports[`Member expression type combinations: Invalid type combinations return TypeError 81`] = `
Object {
"elaborate": "Expected array index as prop, got other number.",
"explain": "Expected array index as prop, got other number.",
"left": Array [
2,
],
"right": 0.5,
}
`;

exports[`Member expression type combinations: Invalid type combinations return TypeError 82`] = `
Object {
"elaborate": "Expected array index as prop, got other number.",
"explain": "Expected array index as prop, got other number.",
"left": Array [
2,
],
"right": 4294967295,
}
`;

exports[`Ternary/if test expression type combinations: Invalid type combinations return TypeError 1`] = `
Object {
"elaborate": "Expected boolean as condition, got number.",
Expand Down
8 changes: 8 additions & 0 deletions src/utils/__tests__/rttc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,14 @@ describe('Member expression type combinations:', () => {
})
})

// Extra tests for array indices integral check.
valid.push([arr, 0])
valid.push([arr, 10])
valid.push([arr, 2 ** 32 - 2])
invalid.push([arr, -1])
invalid.push([arr, 0.5])
invalid.push([arr, 2 ** 32 - 1])

test('Valid type combinations are OK', () => {
valid.forEach(([left, right]: [Value, Value]) => {
const context = mockRuntimeContext()
Expand Down
10 changes: 9 additions & 1 deletion src/utils/rttc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ const typeOf = (v: Value) => {
}

const isNumber = (v: Value) => typeOf(v) === 'number'
// See section 4 of https://2ality.com/2012/12/arrays.html
// v >>> 0 === v checks that v is a valid unsigned 32-bit int
// tslint:disable-next-line:no-bitwise
const isArrayIndex = (v: Value) => isNumber(v) && v >>> 0 === v && v < 2 ** 32 - 1
const isString = (v: Value) => typeOf(v) === 'string'
const isBool = (v: Value) => typeOf(v) === 'boolean'
const isObject = (v: Value) => typeOf(v) === 'object'
Expand Down Expand Up @@ -95,7 +99,11 @@ export const checkMemberAccess = (node: es.Node, obj: Value, prop: Value) => {
if (isObject(obj)) {
return isString(prop) ? undefined : new TypeError(node, ' as prop', 'string', typeOf(prop))
} else if (isArray(obj)) {
return isNumber(prop) ? undefined : new TypeError(node, ' as prop', 'number', typeOf(prop))
return isArrayIndex(prop)
? undefined
: isNumber(prop)
? new TypeError(node, ' as prop', 'array index', 'other number')
: new TypeError(node, ' as prop', 'array index', typeOf(prop))
} else {
return new TypeError(node, '', 'object or array', typeOf(obj))
}
Expand Down