Skip to content

Commit

Permalink
Some fixes to [] overloading
Browse files Browse the repository at this point in the history
  • Loading branch information
littledan committed Dec 23, 2018
1 parent ea7216f commit a5eca29
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 13 deletions.
32 changes: 21 additions & 11 deletions babel/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const binaryOperators = ['-', '*', '/', '%', '**', '&', '^', '|', '<<', '>>', '>
const binaryOperatorSet = new Set(binaryOperators);
const unaryOperators = ['pos', 'neg', '++', '--', '~'];
const unaryOperatorSet = new Set(unaryOperators);
const allOperators = binaryOperators.concat(unaryOperators);
const allOperators = binaryOperators.concat(unaryOperators, ['[]', '[]=']);
const operatorSet = new Set(allOperators);

// To implement operators on built-in types, back them by
Expand Down Expand Up @@ -141,9 +141,11 @@ function partitionTables(tables) {
`the operator ${key} may not be overloaded on the provided type`);
}
}
left[leftSet.OperatorCounter] = table;
// "Backwards" because this new operator type is on the right
// and the other argument is on the left
right[leftSet.OperatorCounter] = table;
} else {
if (typeof rightType !== 'undefined') {
if (typeof rightType === 'undefined') {
throw new TypeError('Either left: or right: must be provided');
}
const rightSet = rightType[OperatorDefinition];
Expand All @@ -157,7 +159,7 @@ function partitionTables(tables) {
`the operator ${key} may not be overloaded on the provided type`);
}
}
right[rightSet.OperatorCounter] = table;
left[rightSet.OperatorCounter] = table;
}
}
return {left, right};
Expand Down Expand Up @@ -226,8 +228,12 @@ export function Operators(table, ...tables) {
// so that the receiver will be accurate (e.g., in case it uses private)
const proxy = new Proxy({[OperatorSet]: set}, {
getOwnPropertyDescriptor(target, key) {
const value = get(target, key);
if (value === sentinel) return undefined;
const n = CanonicalNumericIndexString(key);
if (n === undefined) return Reflect.getOwnPropertyDescriptor(target, key, proxy);
if (IsBadIndex(n)) return undefined;
const length = Number(proxy.length);
if (n >= length) return undefined;
const value = table['[]'](proxy, n);
return {value, writable: true, enumerable: true, configurable: false};
},
has(target, key) {
Expand All @@ -248,13 +254,17 @@ export function Operators(table, ...tables) {
return true;
},
get(target, key) {
const value = get(target, key);
if (value === sentinel) return undefined;
const n = CanonicalNumericIndexString(key);
if (n === undefined) return Reflect.get(target, key, proxy);
if (IsBadIndex(n)) return undefined;
const length = Number(proxy.length);
if (n >= length) return undefined;
const value = table['[]'](proxy, n);
return value;
},
set(target, key, value) {
const n = CanonicalNumericIndexString(key);
if (n === undefined) return Reflect.defineProperty(target, key, desc, proxy);
if (n === undefined) return Reflect.set(target, key, value, proxy);
if (IsBadIndex(n)) return false;
table['[]='](proxy, n, value);
return true;
Expand All @@ -267,7 +277,7 @@ export function Operators(table, ...tables) {
return keys;
},
});
return new Proxy({[OperatorSet]: set}, indexHandler);
return proxy;
}
};
} else {
Expand Down Expand Up @@ -402,7 +412,7 @@ function dispatchBinaryOperator(operator, a, b, operatorSet) {
definitions = b[OperatorSet].RightOperatorDefinitions[
a[OperatorSet].OperatorCounter];
} else {
definitions = a[OperatorSet].RightOperatorDefinitions[
definitions = a[OperatorSet].LeftOperatorDefinitions[
b[OperatorSet].OperatorCounter];
}
if (typeof definitions !== 'object') {
Expand Down
56 changes: 54 additions & 2 deletions babel/spec/runtime/runtime-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ describe('simple overloading', () => {
});
});

describe('overloading with interoperation', () => {
describe('overloading on the right', () => {

const Ops = runtime.Operators({ }, { left: Number,
'*'(a, b) {
Expand All @@ -165,10 +165,62 @@ describe('overloading with interoperation', () => {
it('Number*Vector is permitted, other combinations banned', () => {
const operators = runtime._declareOperators();
runtime._withOperatorsFrom(operators, Vector);
// The following line fails
expect(runtime._numericBinaryOperate('*', 2, vec, operators).contents[2]).toBe(6);
expect(() => runtime._numericBinaryOperate('*', vec, vec, operators)).toThrowError(TypeError);
expect(() => runtime._numericBinaryOperate('*', vec, 2, operators)).toThrowError(TypeError);
expect(runtime._numericBinaryOperate('*', 2, 2, operators)).toBe(4);
});
});

describe('overloading on the left', () => {

const Ops = runtime.Operators({ }, { right: Number,
'*'(a, b) {
return new Vector(a.contents.map(elt => b * elt));
}
});

class Vector extends Ops {
constructor(contents) { super(); this.contents = contents; }
}

const vec = new Vector([1, 2, 3]);

it('* throws when not in operator set', () => {
const operators = runtime._declareOperators();
expect(() => runtime._numericBinaryOperate('*', 2, vec, operators)).toThrowError(TypeError);
});

it('Number*Vector is permitted, other combinations banned', () => {
const operators = runtime._declareOperators();
runtime._withOperatorsFrom(operators, Vector);
expect(() => runtime._numericBinaryOperate('*', 2, vec, operators)).toThrowError(TypeError);
expect(() => runtime._numericBinaryOperate('*', vec, vec, operators)).toThrowError(TypeError);
expect(runtime._numericBinaryOperate('*', vec, 2, operators).contents[2]).toBe(6);
expect(runtime._numericBinaryOperate('*', 2, 2, operators)).toBe(4);
});
});

describe('[] overloading', () => {
const Ops = runtime.Operators({
'[]'(a, b) {
return a.contents[b];
},
'[]='(a, b, c) {
a.contents[b] = c;
}
});

class Vector extends Ops {
constructor(contents) { super(); this.contents = contents; }
}


it('Vector[Number] access works', () => {
const vec = new Vector([1, 2, 3]);
expect(vec[0]).toBe(1);
expect(vec[1]).toBe(2);
expect(vec[2]).toBe(3);
expect(vec[3]).toBe(undefined);
});
});

0 comments on commit a5eca29

Please sign in to comment.