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
45 changes: 35 additions & 10 deletions gs/reflect/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,10 @@ function convertValue(x: ReflectValue, ft: Type, t: Type): ReflectValue {
}

function internType(t: Type): Type {
// A canonical type interns to itself, so it needs no identity key.
if (internedTypes.has(t)) {
return t
}
const key = typeIdentityKey(t)
$.markReflectTypeIdentity(t, key)
const existing = canonicalTypes.get(key)
Expand All @@ -309,9 +313,13 @@ function internType(t: Type): Type {
return existing
}
canonicalTypes.set(key, t)
internedTypes.add(t)
return t
}

// internedTypes holds the canonical types, which internType returned.
const internedTypes = new WeakSet<Type>()

function mergeTypeMetadata(target: Type, source: Type): void {
if (target instanceof BasicType && source instanceof BasicType) {
target.mergeMethodSignatures(source.methodSignatures())
Expand Down Expand Up @@ -805,14 +813,15 @@ export class Value {
}

public clone(): Value {
const cloned = new Value(
this._value,
this._type,
this._parentVarRef,
this._parentStruct,
this._fieldName,
this._interfaceValue,
)
// The fields of a constructed Value are already normalized, so the copy
// takes them as they are rather than checking them again.
const cloned = new Value()
cloned._value = this._value
cloned._type = this._type
cloned._parentVarRef = this._parentVarRef
cloned._parentStruct = this._parentStruct
cloned._fieldName = this._fieldName
cloned._interfaceValue = this._interfaceValue
cloned._addressable = this._addressable
return cloned
}
Expand Down Expand Up @@ -5575,13 +5584,29 @@ export function ArrayOf(length: number, elem: Type): Type {
return internType(new ArrayType(elem, length))
}

// sliceTypes and pointerTypes cache the slice and pointer type of each
// type, which SliceOf and PointerTo would otherwise intern again on every
// call, as reflect.New does.
const sliceTypes = new WeakMap<Type, Type>()
const pointerTypes = new WeakMap<Type, Type>()

export function SliceOf(t: Type): Type {
return internType(new SliceType(t))
let st = sliceTypes.get(t)
if (st === undefined) {
st = internType(new SliceType(t))
sliceTypes.set(t, st)
}
return st
}

export function PointerTo(t: Type | null): Type | null {
if (t === null) return null
return internType(new PointerType(t))
let pt = pointerTypes.get(t)
if (pt === undefined) {
pt = internType(new PointerType(t))
pointerTypes.set(t, pt)
}
return pt
}

export function PtrTo(t: Type | null): Type | null {
Expand Down
43 changes: 31 additions & 12 deletions gs/reflect/value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,22 +109,32 @@ function newStructPointer(typ: Type): ReflectValue {
// newStructValue returns a new zero struct of the type typ, which has no
// registered constructor, such as an anonymous struct type.
function newStructValue(typ: Type): ReflectValue {
type StructValueInstance = Record<string, ReflectValue>
const StructValue = structValueClass(typ)
return new StructValue() as ReflectValue
}

// The fields are own properties of the struct object, starting at zero.
const initFields = (value: StructValueInstance): void => {
for (let i = 0; i < typ.NumField(); i++) {
const field = typ.Field(i)
const key = structFieldStorageKey(typ, i)
value[key] = zeroReflectValue(field.Type!)
}
type StructValueInstance = Record<string, ReflectValue>

// structValueClasses caches the class of the values of each struct type
// without a registered constructor, so its values share one shape.
const structValueClasses = new WeakMap<Type, new () => StructValueInstance>()

// structValueClass returns the class of the values of the struct type typ.
// Like a generated struct class, its constructor makes a zero value.
function structValueClass(typ: Type): new () => StructValueInstance {
const cached = structValueClasses.get(typ)
if (cached !== undefined) {
return cached
}

// A clone copies each field.
const StructValue = class {
constructor() {
initStructFields(typ, this as unknown as StructValueInstance)
}

public clone(): unknown {
const cloned = new StructValue() as unknown as StructValueInstance
initFields(cloned)
for (const key of Object.keys(this)) {
cloned[key] = (this as unknown as StructValueInstance)[key]
}
Expand All @@ -135,10 +145,19 @@ function newStructValue(typ: Type): ReflectValue {
Object.defineProperty(StructValue, '__typeInfo', {
value: typeInfoFromReflectType(typ),
})
const ctor = StructValue as unknown as new () => StructValueInstance
structValueClasses.set(typ, ctor)
return ctor
}

const value = new StructValue() as unknown as StructValueInstance
initFields(value)
return value as ReflectValue
// initStructFields sets the fields of the struct value of type typ to zero,
// as own properties of the struct object.
function initStructFields(typ: Type, value: StructValueInstance): void {
for (let i = 0; i < typ.NumField(); i++) {
const field = typ.Field(i)
const key = structFieldStorageKey(typ, i)
value[key] = zeroReflectValue(field.Type!)
}
}

// Copy copies the contents of src to dst until either dst has been filled
Expand Down
Loading