add noms.assert module

This commit is contained in:
Rafael Weinstein
2015-11-03 14:47:27 -08:00
parent 003bfd5545
commit a96f7743fe
9 changed files with 184 additions and 205 deletions

14
js2/src/assert.js Normal file
View File

@@ -0,0 +1,14 @@
/* @flow */
'use strict';
export function invariant(exp: boolean, message: string = 'Invariant violated') {
if (!exp) {
throw new Error(message);
}
}
export function notNull<T>(v: ?T): T {
invariant(v !== null && v !== undefined, 'Unexpected null value');
return v;
}

35
js2/src/assert_test.js Normal file
View File

@@ -0,0 +1,35 @@
/* @flow */
'use strict';
import {suite, test} from 'mocha';
import {assert} from 'chai';
import {invariant, notNull} from './assert.js';
class Foo {
doNothing() {}
}
function doSomething() {}
suite('assert', () => {
test('invariant', () => {
invariant(true);
assert.throws(() => {
invariant(false);
});
});
test('notNull', () => {
let t: ?Foo = null;
assert.throws(() => {
notNull(t);
});
t = new Foo();
doSomething(); // might have nullified t;
let t2: Foo = notNull(t); // shouldn't throw
t2.doNothing();
});
});

View File

@@ -4,6 +4,7 @@
import Chunk from './chunk.js';
import Ref from './ref.js';
import {invariant} from './assert.js';
const sha1Size = 20;
const chunkLengthSize = 4; // uint32
@@ -44,9 +45,7 @@ export function deserialize(buffer: ArrayBuffer): Array<Chunk> {
let totalLenth = buffer.byteLength;
for (let offset = 0; offset < totalLenth;) {
if (buffer.byteLength - offset < chunkHeaderSize) {
throw new Error('Invalid chunk buffer');
}
invariant(buffer.byteLength - offset >= chunkHeaderSize, 'Invalid chunk buffer');
let refArray = new Uint8Array(buffer, offset, sha1Size);
let ref = new Ref(new Uint8Array(refArray));
@@ -57,15 +56,12 @@ export function deserialize(buffer: ArrayBuffer): Array<Chunk> {
let chunkLength = sizeArray[0];
offset += chunkLengthSize;
if (offset + chunkLength > totalLenth) {
throw new Error('Invalid chunk buffer');
}
invariant(offset + chunkLength <= totalLenth, 'Invalid chunk buffer');
let dataArray = new Uint8Array(buffer, offset, chunkLength);
let chunk = new Chunk(new Uint8Array(dataArray)); // Makes a slice (copy) of the byte sequence from buffer.
if (!chunk.ref.equals(ref)) {
throw new Error('Serialized ref !== computed ref');
}
invariant(chunk.ref.equals(ref), 'Serialized ref !== computed ref');
offset += chunkLength;
chunks.push(chunk);

View File

@@ -6,9 +6,10 @@ import Chunk from './chunk.js';
import Ref from './ref.js';
import type {ChunkStore} from './chunk_store.js';
import type {NomsKind} from './noms_kind.js';
import {Field, makeCompoundTypeRef, makePrimitiveTypeRef, makeStructTypeRef, makeTypeRef, makeUnresolvedTypeRef, StructDesc, TypeRef} from './type_ref.js';
import {invariant, notNull} from './assert.js';
import {isPrimitiveKind, Kind} from './noms_kind.js';
import {lookupPackage, Package, readPackage} from './package.js';
import {Field, makeCompoundTypeRef, makePrimitiveTypeRef, makeStructTypeRef, makeTypeRef, makeUnresolvedTypeRef, StructDesc, TypeRef} from './type_ref.js';
const typedTag = 't ';
@@ -33,53 +34,39 @@ class JsonArrayReader {
readString(): string {
let next = this.read();
if (typeof next === 'string') {
return next;
}
throw new Error('Serialization error: expected string, got ' + typeof next);
invariant(typeof next === 'string');
return next;
}
readBool(): boolean {
let next = this.read();
if (typeof next === 'boolean') {
return next;
}
throw new Error('Serialization error: expected boolean, got ' + typeof next);
invariant(typeof next === 'boolean');
return next;
}
readNumber(): number {
let next = this.read();
invariant(typeof next === 'number');
return next;
}
readOrdinal(): number {
let next = this.read();
if (typeof next === 'number' && next >= 0) {
return next;
}
throw new Error('Serialization error: expected ordinal, got ' + typeof next);
return this.readNumber();
}
readArray(): Array<any> {
let next = this.read();
if (next instanceof Array) {
return next;
}
throw new Error('Serialization error: expected Array');
invariant(Array.isArray(next));
return next;
}
readKind(): NomsKind {
let next = this.read();
if (typeof next === 'number') {
return next;
}
throw new Error('Serialization error: expected NomsKind, got ' + typeof next);
return this.readNumber();
}
readRef(): Ref {
let next = this.read();
if (typeof next === 'string') {
return Ref.parse(next);
}
throw new Error('Serialization error: expected Ref, got ' + typeof next);
let next = this.readString();
return Ref.parse(next);
}
readTypeRefAsTag(): TypeRef {
@@ -231,20 +218,14 @@ class JsonArrayReader {
}
}
if (pkg) {
let typeDef = pkg.types[ordinal];
if (typeDef.kind === Kind.Enum) {
throw new Error('Not implemented');
}
if (typeDef.kind !== Kind.Struct) {
throw new Error('Attempt to resolve non-struct struct kind');
}
return this.readStruct(typeDef, t, pkg);
} else {
throw new Error('Woah, got a null pkg. pkgRef: ' + pkgRef.toString() + ' ordinal: ' + ordinal);
pkg = notNull(pkg);
let typeDef = pkg.types[ordinal];
if (typeDef.kind === Kind.Enum) {
throw new Error('Not implemented');
}
invariant(typeDef.kind === Kind.Struct);
return this.readStruct(typeDef, t, pkg);
}
readTypeRefAsValue(pkg: ?Package): TypeRef {
@@ -289,53 +270,47 @@ class JsonArrayReader {
if (ordinal === -1) {
let namespace = this.readString();
let name = this.readString();
if (!pkgRef.isEmpty()) {
throw new Error('Unresolved TypeRefs may not have a package ref');
}
invariant(pkgRef.isEmpty(), 'Unresolved TypeRefs may not have a package ref');
return makeUnresolvedTypeRef(namespace, name);
}
return makeTypeRef(pkgRef, ordinal);
}
default: {
if (!isPrimitiveKind(k)) {
throw new Error('Not implemented: ' + k);
}
return makePrimitiveTypeRef(k);
}
}
invariant(isPrimitiveKind(k));
return makePrimitiveTypeRef(k);
}
async readStruct(typeDef: TypeRef, typeRef: TypeRef, pkg: Package): Promise<any> {
// TODO FixupTypeRef?
// TODO Make read of sub-values parallel.
let desc = typeDef.desc;
if (desc instanceof StructDesc) {
let s: { [key: string]: any } = Object.create(null);
s._typeRef = typeDef; // TODO: Need a better way to add typeRef
invariant(desc instanceof StructDesc);
for (let field of desc.fields) {
if (field.optional) {
let b = this.readBool();
if (b) {
let v = await this.readValueWithoutTag(field.t, pkg);
s[field.name] = v;
}
} else {
let s: { [key: string]: any } = Object.create(null);
s._typeRef = typeDef; // TODO: Need a better way to add typeRef
for (let field of desc.fields) {
if (field.optional) {
let b = this.readBool();
if (b) {
let v = await this.readValueWithoutTag(field.t, pkg);
s[field.name] = v;
}
} else {
let v = await this.readValueWithoutTag(field.t, pkg);
s[field.name] = v;
}
if (desc.union.length > 0) {
throw new Error('Not implemented');
}
return s;
} else {
throw new Error('Attempt to read struct without StructDesc typeref');
}
if (desc.union.length > 0) {
throw new Error('Not implemented');
}
return s;
}
}

View File

@@ -7,9 +7,10 @@ import MemoryStore from './memory_store.js';
import Ref from './ref.js';
import type {ChunkStore} from './chunk_store.js';
import type {NomsKind} from './noms_kind.js';
import {invariant} from './assert.js';
import {isPrimitiveKind, Kind} from './noms_kind.js';
import {makePrimitiveTypeRef, StructDesc, TypeRef} from './type_ref.js';
import {lookupPackage, Package} from './package.js';
import {makePrimitiveTypeRef, StructDesc, TypeRef} from './type_ref.js';
const typedTag = 't ';
@@ -77,76 +78,57 @@ class JsonArrayWriter {
this.write(v); // TODO: Verify value fits in type
break;
case Kind.List: {
if (v instanceof Array) {
let w2 = new JsonArrayWriter(this._cs);
let elemType = t.elemTypes[0];
v.forEach(sv => w2.writeValue(sv, elemType));
this.write(w2.array);
} else {
throw new Error('Attempt to serialize non-list as list');
}
invariant(Array.isArray(v));
let w2 = new JsonArrayWriter(this._cs);
let elemType = t.elemTypes[0];
v.forEach(sv => w2.writeValue(sv, elemType));
this.write(w2.array);
break;
}
case Kind.Map: {
if (v instanceof Map) {
let w2 = new JsonArrayWriter(this._cs);
let keyType = t.elemTypes[0];
let valueType = t.elemTypes[1];
let elems = [];
v.forEach((v, k) => {
elems.push(k);
});
elems = orderValuesByRef(keyType, elems);
elems.forEach(elem => {
w2.writeValue(elem, keyType);
w2.writeValue(v.get(elem), valueType);
});
this.write(w2.array);
} else {
throw new Error('Attempt to serialize non-map as maps');
}
invariant(v instanceof Map);
let w2 = new JsonArrayWriter(this._cs);
let keyType = t.elemTypes[0];
let valueType = t.elemTypes[1];
let elems = [];
v.forEach((v, k) => {
elems.push(k);
});
elems = orderValuesByRef(keyType, elems);
elems.forEach(elem => {
w2.writeValue(elem, keyType);
w2.writeValue(v.get(elem), valueType);
});
this.write(w2.array);
break;
}
case Kind.Package: {
if (v instanceof Package) {
let ptr = makePrimitiveTypeRef(Kind.TypeRef);
let w2 = new JsonArrayWriter(this._cs);
v.types.forEach(type => w2.writeValue(type, ptr));
this.write(w2.array);
let w3 = new JsonArrayWriter(this._cs);
v.dependencies.forEach(ref => w3.writeRef(ref));
this.write(w3.array);
} else {
throw new Error('Attempt to serialize non-package as package');
}
invariant(v instanceof Package);
let ptr = makePrimitiveTypeRef(Kind.TypeRef);
let w2 = new JsonArrayWriter(this._cs);
v.types.forEach(type => w2.writeValue(type, ptr));
this.write(w2.array);
let w3 = new JsonArrayWriter(this._cs);
v.dependencies.forEach(ref => w3.writeRef(ref));
this.write(w3.array);
break;
}
case Kind.Set: {
if (v instanceof Set) {
let w2 = new JsonArrayWriter(this._cs);
let elemType = t.elemTypes[0];
let elems = [];
v.forEach(v => {
elems.push(v);
});
elems = orderValuesByRef(elemType, elems);
elems.forEach(elem => w2.writeValue(elem, elemType));
this.write(w2.array);
} else {
throw new Error('Attempt to serialize non-set as set');
}
invariant(v instanceof Set);
let w2 = new JsonArrayWriter(this._cs);
let elemType = t.elemTypes[0];
let elems = [];
v.forEach(v => {
elems.push(v);
});
elems = orderValuesByRef(elemType, elems);
elems.forEach(elem => w2.writeValue(elem, elemType));
this.write(w2.array);
break;
}
case Kind.TypeRef:
if (v instanceof TypeRef) {
this.writeTypeRefAsValue(v);
} else {
throw new Error('Attempt to serialize non-typeref as typeref');
}
invariant(v instanceof TypeRef);
this.writeTypeRefAsValue(v);
break;
default:
throw new Error('Not implemented');
@@ -171,26 +153,22 @@ class JsonArrayWriter {
}
case Kind.Struct: {
let desc = t.desc;
if (desc instanceof StructDesc) {
this.write(t.name);
let fieldWriter = new JsonArrayWriter(this._cs);
desc.fields.forEach(field => {
fieldWriter.write(field.name);
fieldWriter.writeTypeRefAsValue(field.t);
fieldWriter.write(field.optional);
});
this.write(fieldWriter.array);
let choiceWriter = new JsonArrayWriter(this._cs);
desc.union.forEach(choice => {
choiceWriter.write(choice.name);
choiceWriter.writeTypeRefAsValue(choice.t);
choiceWriter.write(choice.optional);
});
this.write(choiceWriter.array);
} else {
throw new Error('Attempt to serialize non-struct typeref as struct type-ref');
}
invariant(desc instanceof StructDesc);
this.write(t.name);
let fieldWriter = new JsonArrayWriter(this._cs);
desc.fields.forEach(field => {
fieldWriter.write(field.name);
fieldWriter.writeTypeRefAsValue(field.t);
fieldWriter.write(field.optional);
});
this.write(fieldWriter.array);
let choiceWriter = new JsonArrayWriter(this._cs);
desc.union.forEach(choice => {
choiceWriter.write(choice.name);
choiceWriter.writeTypeRefAsValue(choice.t);
choiceWriter.write(choice.optional);
});
this.write(choiceWriter.array);
break;
}
case Kind.Unresolved: {
@@ -211,9 +189,7 @@ class JsonArrayWriter {
}
default: {
if (!isPrimitiveKind(k)) {
throw new Error('Not implemented.');
}
invariant(isPrimitiveKind(k));
}
}
}
@@ -233,11 +209,11 @@ function orderValuesByRef(t: TypeRef, a: Array<any>): Array<any> {
}
function encodeNomsValue(v: any, t: TypeRef): Chunk {
// if (v instanceof Package) {
// if (v.dependencies.length > 0) {
// throw new Error('Not implemented');
// }
// }
if (v instanceof Package) {
if (v.dependencies.length > 0) {
throw new Error('Not implemented');
}
}
let ms = new MemoryStore(); // TODO: This should be passed in.
let w = new JsonArrayWriter(ms);

View File

@@ -6,6 +6,7 @@ import Chunk from './chunk.js';
import fetch from 'isomorphic-fetch';
import Ref from './ref.js';
import {deserialize} from './chunk_serializer.js';
import {invariant} from './assert.js';
type ReadRequest = {
resolve: (c: Chunk) => void,
@@ -94,9 +95,7 @@ export default class HttpStore {
}
});
if (response.status !== 200) {
throw new Error('Buffered read failed: ' + response.status);
}
invariant(response.status === 200, 'Buffered read failed: ' + response.status);
let blob = await response.blob();
let buffer = await blobToBuffer(blob);

View File

@@ -6,16 +6,16 @@ import Chunk from './chunk.js';
import HttpStore from './http_store.js';
import MemoryStore from './memory_store.js';
import Ref from './ref.js';
import {readValue} from './decode.js';
import {encodeNomsValue} from './encode.js';
import {readValue} from './decode.js';
import {TypeRef} from './type_ref.js';
export {
Chunk,
readValue,
encodeNomsValue,
HttpStore,
MemoryStore,
readValue,
Ref,
TypeRef
};

View File

@@ -2,12 +2,13 @@
'use strict';
import {readValue} from './decode.js';
import Ref from './ref.js';
import type {ChunkStore} from './chunk_store.js';
import {encodeNomsValue} from './encode.js';
import {invariant} from './assert.js';
import {Kind} from './noms_kind.js';
import {makePrimitiveTypeRef, TypeRef} from './type_ref.js';
import {readValue} from './decode.js';
const packageTypeRef = makePrimitiveTypeRef(Kind.Package);
@@ -46,12 +47,9 @@ function registerPackage(p: Package) {
async function readPackage(r: Ref, cs: ChunkStore): Promise<Package> {
let p = await readValue(r, cs);
if (p instanceof Package) {
registerPackage(p);
return p;
} else {
throw new Error('Non-package found where package expected.');
}
invariant(p instanceof Package);
registerPackage(p);
return p;
}
export {lookupPackage, Package, readPackage, registerPackage};

View File

@@ -2,9 +2,10 @@
'use strict';
import {isPrimitiveKind, Kind} from './noms_kind.js';
import Ref from './ref.js';
import type {NomsKind} from './noms_kind.js';
import {invariant} from './assert.js';
import {isPrimitiveKind, Kind} from './noms_kind.js';
type TypeDesc = {
kind: NomsKind;
@@ -161,19 +162,13 @@ class TypeRef {
}
get packageRef(): Ref {
if (this._desc instanceof UnresolvedDesc) {
return this._desc._pkgRef;
}
throw new Error('PackageRef only works on unresolved type refs');
invariant(this._desc instanceof UnresolvedDesc);
return this._desc._pkgRef;
}
get ordinal(): number {
if (this._desc instanceof UnresolvedDesc) {
return this._desc._ordinal;
}
throw new Error('Ordinal has not been set');
invariant(this._desc instanceof UnresolvedDesc);
return this._desc._ordinal;
}
get name(): string {
@@ -198,11 +193,8 @@ class TypeRef {
}
get elemTypes(): Array<TypeRef> {
if (this._desc instanceof CompoundDesc) {
return this._desc.elemTypes;
}
throw new Error('Only CompoundDesc have elemTypes');
invariant(this._desc instanceof CompoundDesc);
return this._desc.elemTypes;
}
}
@@ -232,16 +224,10 @@ function makePrimitiveTypeRef(k: NomsKind): TypeRef {
function makeCompoundTypeRef(k: NomsKind, ...elemTypes: Array<TypeRef>): TypeRef {
if (elemTypes.length === 1) {
if (k === Kind.Map) {
throw new Error('Map requires 2 element types');
}
invariant(k !== Kind.Map, 'Map requires 2 element types');
} else {
if (k !== Kind.Map) {
throw new Error('Only Map can have multiple element types');
}
if (elemTypes.length !== 2) {
throw new Error('Map requires 2 element types');
}
invariant(k === Kind.Map, 'Only Map can have multiple element types');
invariant(elemTypes.length === 2, 'Map requires 2 element types');
}
return buildType('', new CompoundDesc(k, elemTypes));