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
27 changes: 17 additions & 10 deletions gs/builtin/builtin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,22 +255,29 @@ export function genericPointerRef<T>(
* variable's address.
*/
export function pointerValue<T>(value: T | VarRef<T> | null | undefined): T {
// A variable reference holds its value, and a struct that is not a boxed
// pointer is its own target. These property reads are much faster than
// the type and 'in' checks of pointerValueSlow, on the many kinds of
// objects seen here, and keep this function small enough to be inlined.
if (value !== null && value !== undefined) {
if ((value as any).__isVarRef === true) {
return (value as VarRef<T>).value
}
if ((value as any).__goValue === undefined) {
return value as T
}
}
return pointerValueSlow(value)
}

// pointerValueSlow is pointerValue for nil and boxed pointers.
function pointerValueSlow<T>(value: T | VarRef<T> | null | undefined): T {
if (value === null || value === undefined) {
runtimePanic(
'runtime error: invalid memory address or nil pointer dereference',
)
}

// A variable reference holds its value, and a struct that is not a boxed
// pointer is its own target. These property reads are much faster than
// the type and 'in' checks below, on the many kinds of objects seen here.
if ((value as any).__isVarRef === true) {
return (value as VarRef<T>).value
}
if ((value as any).__goValue === undefined) {
return value as T
}

// A boxed pointer is unwrapped to its target.
const boxed =
typeof value === 'object' && value !== null && '__goValue' in value ?
Expand Down
74 changes: 62 additions & 12 deletions gs/builtin/slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,22 @@ export function sliceRange(
): SliceRange<number>
export function sliceRange<T>(
slice: Slice<T> | TypedSlice | null | undefined,
): SliceRange<T> | SliceRange<number> {
// A window object is its own window, and a plain array is a window over
// itself. They are handled here, in a function small enough for engines
// to inline, which also lets them avoid allocating the window of an array.
if (slice instanceof GoSlice) {
return slice
}
if (Array.isArray(slice) && (slice as any).__meta__ === undefined) {
return { backing: slice, offset: 0, length: slice.length }
}
return sliceRangeSlow(slice)
}

// sliceRangeSlow is sliceRange for nil, typed and proxied slices.
function sliceRangeSlow<T>(
slice: Slice<T> | TypedSlice | null | undefined,
): SliceRange<T> | SliceRange<number> {
// A nil slice has no elements.
if (slice === null || slice === undefined) {
Expand Down Expand Up @@ -1325,25 +1341,29 @@ export function arrayToSlice<T>(
export function sliceLen(
slice: Slice<unknown> | unknown[] | TypedSlice | null | undefined,
): number {
// A nil slice has no elements.
if (slice === null || slice === undefined) {
return 0
}

// A window object and a plain array are the common cases, tested first
// without reading __meta__, which is slow on the many kinds of objects
// seen here. An array proxy (a window viewed as a Go array) has __meta__.
// A window object, a plain array and a typed slice are the common cases,
// tested here without reading __meta__, which is slow on the many kinds of
// objects seen here, in a function small enough for engines to inline. An
// array proxy (a window viewed as a Go array) has __meta__.
if (slice instanceof GoSlice) {
return slice.length
}
if (ArrayBuffer.isView(slice)) {
return (slice as TypedSlice).length
}
if (Array.isArray(slice) && (slice as any).__meta__ === undefined) {
return slice.length
}
return sliceLenSlow(slice)
}

// A typed slice is a view of exactly its elements, whose window, if any,
// has the same length.
if (isTypedSlice(slice)) {
return slice.length
// sliceLenSlow is sliceLen for nil slices, proxies and windowed arrays.
function sliceLenSlow(
slice: Slice<unknown> | unknown[] | TypedSlice | null | undefined,
): number {
// A nil slice has no elements.
if (slice === null || slice === undefined) {
return 0
}

// A slice proxy knows its length.
Expand Down Expand Up @@ -2332,6 +2352,36 @@ export function arrayIndex<
| TypedSlice
| null
| undefined,
>(collection: C, index: number): ArrayIndexValue<C> {
// An index in range of a window object, a typed slice or a plain array is
// read here, in a function small enough for engines to inline into hot
// loops; arrayIndexSlow handles the other representations and panics.
if (collection instanceof GoSlice) {
if (index >= 0 && index < collection.length) {
return collection.backing[collection.offset + index] as ArrayIndexValue<C>
}
} else if (ArrayBuffer.isView(collection)) {
if (index >= 0 && index < (collection as TypedSlice).length) {
return (collection as TypedSlice)[index] as ArrayIndexValue<C>
}
} else if (Array.isArray(collection) && (collection as any).__meta__ === undefined) {
if (index >= 0 && index < collection.length) {
return collection[index] as ArrayIndexValue<C>
}
}
return arrayIndexSlow(collection, index)
}

// arrayIndexSlow is arrayIndex for the representations and indexes that its
// fast path does not handle.
function arrayIndexSlow<
C extends
| Slice<unknown>
| SliceProxy<unknown>
| unknown[]
| TypedSlice
| null
| undefined,
>(collection: C, index: number): ArrayIndexValue<C> {
if (collection === null || collection === undefined) {
outOfRangeIndex(index, 0)
Expand Down
8 changes: 7 additions & 1 deletion gs/builtin/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1948,10 +1948,16 @@ function markClonedStructValue<T>(copy: T): T {
// value unchanged. A marked struct with no clone method is returned unchanged
// rather than panicking.
export function copyElement<T>(value: T): T {
// A value that is not an object is copied by value.
// A value that is not an object is copied by value, here in a function
// small enough for engines to inline; copyObjectElement copies the others.
if (typeof value !== 'object' || value === null) {
return value
}
return copyObjectElement(value)
}

// copyObjectElement is copyElement for a value that is an object.
function copyObjectElement<T>(value: T & object): T {

// A struct value of a class is copied as its class copies it.
if ((value as any)[structValueMarker] === true) {
Expand Down
Loading