Skip to content

Commit bfc2c8a

Browse files
committed
Add T.Object, T.Array and T.HashMap
1 parent 6809b7c commit bfc2c8a

File tree

15 files changed

+1045
-7
lines changed

15 files changed

+1045
-7
lines changed

interfaces/types.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,18 +116,40 @@ declare class TypedStruct<S> extends TypedEntity {
116116
static ref: ReferenceType<TypedStruct<S>>;
117117
}
118118

119+
declare class TypedObject<S> extends TypedEntity {
120+
static [symbol: Symbol]: any;
121+
static cast (input: any): TypedObject<S>;
122+
static accepts (input: any): boolean;
123+
static initialize (backing: Backing, address: float64, initialValue?: S|TypedObject<S>): void;
124+
static store (backing: Backing, address: float64, value: S|TypedObject<S>): void;
125+
static load (backing: Backing, address: float64): TypedObject<S>;
126+
static clear (backing: Backing, address: float64): void;
127+
static destructor (backing: Backing, address: float64): void;
128+
static emptyValue (): TypedObject<S>;
129+
static randomValue (): TypedObject<S>;
130+
static hashValue (input: TypedObject<S>|S): uint32;
131+
static equal (valueA: TypedObject<S>|S, valueB: TypedObject<S>|S): boolean;
132+
static compareValues (valueA: TypedObject<S>|S, valueB: TypedObject<S>|S): int8;
133+
static compareAddresses (backing: Backing, addressA: float64, addressB: float64): int8;
134+
static compareAddressValue (backing: Backing, address: float64, value: TypedObject<S>|S): int8;
135+
static Array: Class<ArrayType<TypedObject<S>>>;
136+
static ref: ReferenceType<TypedObject<S>>;
137+
}
138+
119139
declare type PrimitiveType<T> = Class<TypedPrimitive<T>>;
120140
declare type ReferenceType<T> = Class<TypedReference<T>>;
121141
declare type ArrayType<E> = Class<TypedArray<E>>;
122142
declare type HashMapType<K, V> = Class<TypedHashMap<K, V>>;
123143
declare type StructType<S> = Class<TypedStruct<S>>;
144+
declare type ObjectType<S> = Class<TypedObject<S>>;
124145

125146
declare type Type = Class<
126147
TypedPrimitive<any>
127148
| TypedReference<any>
128149
| TypedArray<any>
129150
| TypedHashMap<any, any>
130151
| TypedStruct<any>
152+
| TypedObject<Object>
131153
>;
132154

133155
declare type PartialType<T> = {

src/builtins/any/index.js

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/* @flow */
2+
3+
import hashInteger from "../../hash-functions/integer";
4+
5+
import type Backing from "backing";
6+
import type {Realm} from "../..";
7+
import {$Backing, $Address, $CanBeEmbedded, $CanBeReferenced, $CanContainReferences} from "../../symbols";
8+
9+
export function make (realm: Realm): PrimitiveType<any> {
10+
const {PrimitiveType} = realm;
11+
const Any = new PrimitiveType({
12+
name: 'Any',
13+
byteAlignment: 8,
14+
byteLength: 16,
15+
cast (input: any): any {
16+
if (typeof input === 'function' || typeof input === 'symbol') {
17+
throw new TypeError(`Cannot store values with type: ${typeof input}`);
18+
}
19+
else if (input === undefined) {
20+
return null;
21+
}
22+
else {
23+
return input;
24+
}
25+
},
26+
accepts (input: any): boolean {
27+
if (typeof input === 'function' || typeof input === 'symbol') {
28+
return false;
29+
}
30+
else {
31+
return true;
32+
}
33+
},
34+
initialize (backing: Backing, address: float64, initialValue: any): void {
35+
const Type = realm.typeOf(initialValue);
36+
let initialAddress;
37+
if (Type === null) {
38+
backing.setFloat64(address, 0);
39+
backing.setFloat64(address + 8, 0);
40+
}
41+
else if (Type.byteLength <= 8) {
42+
backing.setFloat64(address, Type.id);
43+
Type.initialize(backing, address + 8, initialValue);
44+
}
45+
else if (initialValue[$Backing] === backing && (initialAddress = initialValue[$Address])) {
46+
backing.setFloat64(address, Type.id);
47+
backing.setFloat64(address + 8, initialAddress);
48+
backing.gc.ref(initialAddress);
49+
}
50+
else {
51+
backing.setFloat64(address, Type.id);
52+
const target = backing.gc.alloc(Type.byteLength, Type.id, 1);
53+
Type.initialize(backing, target, initialValue);
54+
backing.setFloat64(address + 8, target);
55+
}
56+
},
57+
store (backing: Backing, address: float64, value: any): void {
58+
const Type = realm.typeOf(value);
59+
const existingTypeId = backing.getFloat64(address);
60+
if (existingTypeId !== 0) {
61+
const existingType = realm.I[existingTypeId];
62+
63+
if (existingType === Type && Type.byteLength <= 8) {
64+
Type.store(backing, address + 8, value);
65+
return; // nothing left to do.
66+
}
67+
else if (existingType.byteLength <= 8) {
68+
existingType.clear(backing, address + 8);
69+
}
70+
else {
71+
const pointer = backing.getFloat64(address + 8);
72+
if (pointer !== 0) {
73+
backing.gc.unref(pointer);
74+
backing.setFloat64(address + 8, 0); // ensures that the value is cleared, even if store fails.
75+
}
76+
}
77+
}
78+
let valueAddress;
79+
80+
if (Type === null) {
81+
backing.setFloat64(address, 0);
82+
backing.setFloat64(address + 8, 0);
83+
}
84+
else if (Type.byteLength <= 8) {
85+
backing.setFloat64(address, Type.id);
86+
Type.initialize(backing, address + 8, value);
87+
}
88+
else if (value[$Backing] === backing && (valueAddress = value[$Address])) {
89+
backing.setFloat64(address, Type.id);
90+
backing.setFloat64(address + 8, valueAddress);
91+
backing.gc.ref(valueAddress);
92+
}
93+
else {
94+
backing.setFloat64(address, Type.id);
95+
const target = backing.gc.alloc(Type.byteLength, Type.id, 1);
96+
Type.initialize(backing, target, value);
97+
backing.setFloat64(address + 8, target);
98+
}
99+
},
100+
load (backing: Backing, address: float64): any {
101+
const typeId = backing.getFloat64(address);
102+
if (typeId === 0) {
103+
return null;
104+
}
105+
const Type = realm.I[typeId];
106+
if (Type.byteLength <= 8) {
107+
return Type.load(backing, address + 8);
108+
}
109+
const pointer = backing.getFloat64(address + 8);
110+
assert: pointer > 0;
111+
return Type.load(backing, pointer);
112+
},
113+
emptyValue (): any {
114+
return null;
115+
},
116+
randomValue (): any {
117+
return null;
118+
},
119+
hashValue (input): uint32 {
120+
const Type = realm.typeOf(input);
121+
if (Type === null) {
122+
return 4;
123+
}
124+
else {
125+
return Type.hashValue(input);
126+
}
127+
}
128+
});
129+
Any[$CanBeEmbedded] = true;
130+
Any[$CanContainReferences] = true;
131+
Any[$CanBeReferenced] = false;
132+
return Any;
133+
}

src/builtins/any/test.js

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
describeRealm('Builtin: Any', function (options) {
2+
let realm;
3+
let backing;
4+
let T;
5+
let address;
6+
7+
before(() => {
8+
realm = options.realm;
9+
backing = realm.backing;
10+
T = realm.T;
11+
address = backing.alloc(T.Any.byteLength);
12+
});
13+
14+
after(() => {
15+
backing.free(address);
16+
});
17+
18+
describe('Cast', function () {
19+
it('should return an empty value for empty input', function () {
20+
(T.Any() === null).should.equal(true);
21+
});
22+
23+
it('should cast a number', function () {
24+
T.Any(258).should.equal(258);
25+
});
26+
27+
it('should cast a string', function () {
28+
T.Any("123").should.equal("123");
29+
});
30+
31+
it('should cast an empty string', function () {
32+
T.Any("").should.equal("");
33+
});
34+
});
35+
36+
it('should not create a new instance', function () {
37+
(() => new T.Any(true)).should.throw(TypeError);
38+
});
39+
40+
describe('.emptyValue()', function () {
41+
it('should return an empty value', function () {
42+
(T.Any.emptyValue() === null).should.equal(true);
43+
});
44+
});
45+
46+
describe('.randomValue()', function () {
47+
it('should return a random value', function () {
48+
const value = T.Any.randomValue();
49+
(value === null).should.equal(true);
50+
});
51+
52+
it('should accept a random value', function () {
53+
T.Any.accepts(T.Any.randomValue()).should.equal(true);
54+
});
55+
});
56+
57+
describe('.initialize(), .store(), .load() and .clear()', function () {
58+
let SimpleStruct;
59+
let struct;
60+
before(() => {
61+
SimpleStruct = new realm.StructType({a: T.Float64, b: T.Float64, c: T.Float64});
62+
})
63+
it('should initialize an address', function () {
64+
T.Any.initialize(backing, address);
65+
});
66+
67+
it('should load an empty value from a just-initialized address', function () {
68+
(T.Any.load(backing, address) === null).should.equal(true);
69+
});
70+
71+
it('should store a boolean true value at an address', function () {
72+
T.Any.store(backing, address, true);
73+
});
74+
75+
it('should load a boolean true value from an address', function () {
76+
T.Any.load(backing, address).should.equal(true);
77+
});
78+
79+
it('should store a boolean false value at an address', function () {
80+
T.Any.store(backing, address, false);
81+
});
82+
83+
it('should load a boolean false value from an address', function () {
84+
T.Any.load(backing, address).should.equal(false);
85+
});
86+
87+
it('should store a numeric value at an address', function () {
88+
T.Any.store(backing, address, 12345.6);
89+
});
90+
91+
it('should load a numeric value from an address', function () {
92+
T.Any.load(backing, address).should.equal(12345.6);
93+
});
94+
95+
it('should store a string value at an address', function () {
96+
T.Any.store(backing, address, 'hello world');
97+
});
98+
99+
it('should load a string value from an address', function () {
100+
T.Any.load(backing, address).should.equal('hello world');
101+
});
102+
103+
it('should store a typed object at an address', function () {
104+
struct = new SimpleStruct({a: 1, b: 2, c: 3});
105+
T.Any.store(backing, address, struct);
106+
});
107+
108+
it('should load a typed object from an address', function () {
109+
T.Any.load(backing, address).toJSON().should.eql({a: 1, b: 2, c: 3});
110+
});
111+
112+
it('should exactly equal the value', function () {
113+
SimpleStruct.equal(struct, T.Any.load(backing, address)).should.equal(true);
114+
});
115+
116+
it('should store a vanilla object value at an address', function () {
117+
T.Any.store(backing, address, {a: 1, b: 2, c: 3});
118+
});
119+
120+
it('should load a vanilla object value from an address', function () {
121+
T.Any.load(backing, address).should.eql({a: 1, b: 2, c: 3});
122+
});
123+
124+
125+
it('should clear a value from an address', function () {
126+
T.Any.clear(backing, address);
127+
});
128+
129+
it('should load an empty value from a cleaned up address', function () {
130+
(T.Any.load(backing, address) === null).should.equal(true);
131+
});
132+
});
133+
134+
describe('.hashValue()', function () {
135+
it('should hash a value', function () {
136+
T.Any.hashValue(123).should.be.above(-1);
137+
});
138+
139+
it('should hash a true value', function () {
140+
T.Any.hashValue(true).should.be.above(-1);
141+
});
142+
143+
it('should hash an empty value', function () {
144+
T.Any.hashValue(T.Any.emptyValue()).should.be.above(-1);
145+
});
146+
147+
});
148+
});

src/builtins/array/index.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/* @flow */
2+
3+
import type {Realm} from "../..";
4+
5+
export function make ({ArrayType, T}: Realm): ArrayType<any> {
6+
return new ArrayType(T.Any, {
7+
name: 'Array'
8+
});
9+
}

0 commit comments

Comments
 (0)