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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
8 changes: 2 additions & 6 deletions compiler/typescript-emitter.go
Original file line number Diff line number Diff line change
Expand Up @@ -648,8 +648,7 @@ func renderPositionalStructConstructor(b *strings.Builder, structType *loweredSt

// Every instance carries the marker, false for a pointer's struct, so
// that all instances of a class have one shape.
b.WriteString("\t\t;(this as any)[" + runtimeOwner.QualifiedHelper(RuntimeHelperStructValueMarker))
b.WriteString("] = __goscriptValue === true\n\t}\n\n")
b.WriteString("\t\t;(this as any).__goStructValue = __goscriptValue === true\n\t}\n\n")
}

// renderPositionalStructClone emits the clone method of a positional struct
Expand Down Expand Up @@ -709,7 +708,6 @@ const structFreshInitParam = "__goscriptFresh"
// renderStruct emits field storage, value-copy operations, methods, and metadata.
func renderStruct(b *strings.Builder, structType *loweredStruct, runtimeOwner *RuntimeContractOwner, trimTypeInfo bool) {
markStructValue := runtimeOwner.QualifiedHelper(RuntimeHelperMarkAsStructValue)
structValueMarker := runtimeOwner.QualifiedHelper(RuntimeHelperStructValueMarker)
cloneStructValue := runtimeOwner.QualifiedHelper(RuntimeHelperCloneStructValue)
cloneArrayValue := runtimeOwner.QualifiedHelper(RuntimeHelperCloneArrayValue)
copyElement := runtimeOwner.QualifiedHelper(RuntimeHelperCopyElement)
Expand Down Expand Up @@ -833,9 +831,7 @@ func renderStruct(b *strings.Builder, structType *loweredStruct, runtimeOwner *R
// that each class stores it at one site. Every instance has the
// property, false for a pointer's struct, so that all instances of a
// class have one shape and marking one later changes no shape.
b.WriteString("\t\t;(this as any)[")
b.WriteString(structValueMarker)
b.WriteString("] = __goscriptValue === true\n\t}\n\n")
b.WriteString("\t\t;(this as any).__goStructValue = __goscriptValue === true\n\t}\n\n")

// The clone method copies the struct through its constructor.
b.WriteString("\tpublic ")
Expand Down
5 changes: 3 additions & 2 deletions gs/builtin/builtin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { Slice, SliceProxy, StringHeaderData } from './slice.js'
import {
arrayValueTypeInfo,
cloneArrayValue,
ownKeys,
isArrayValue,
isMarkedAsStructValue,
isTypeInfoComparable,
Expand Down Expand Up @@ -581,8 +582,8 @@ function fieldsEqual(
a: Record<string, unknown>,
b: Record<string, unknown>,
): boolean {
const aKeys = Object.keys(a)
const bKeys = Object.keys(b)
const aKeys = ownKeys(a)
const bKeys = ownKeys(b)
if (aKeys.length !== bKeys.length) {
return false
}
Expand Down
3 changes: 2 additions & 1 deletion gs/builtin/map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
canonicalPointerIdentity,
isStructObject,
isStructTypeInfo,
ownKeys,
resolveTypeInfo,
structFieldRuntimeKey,
structValueMarker,
Expand Down Expand Up @@ -394,7 +395,7 @@ function structKeyPlan(key: object): StructKeyField[] {

// The fields are sorted by name so equal keys give equal text.
const kinds = structFieldKinds(key)
const plan = Object.keys(key)
const plan = ownKeys(key)
.sort()
.map((name) => ({ name, kind: kinds?.get(name) }))

Expand Down
4 changes: 2 additions & 2 deletions gs/builtin/print.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { asArray, isSliceProxy, type Slice } from './slice.js'
import { isStructObject } from './type.js'
import { isStructObject, ownKeys } from './type.js'
import { isVarRef } from './varRef.js'

// A transpiled Go Error(), String(), or GoString() method may be async, so
Expand Down Expand Up @@ -210,7 +210,7 @@ function formatObject(
function getObjectEntries(value: Record<string, any>): [string, any][] {
// The fields of a struct are its own properties, functions included.
if (isStructObject(value)) {
return Object.entries(value)
return ownKeys(value).map((key) => [key, value[key]])
}

return Object.entries(value).filter(
Expand Down
22 changes: 18 additions & 4 deletions gs/builtin/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1055,7 +1055,7 @@ function matchesStructType(value: any, info: TypeInfo): boolean {
if (typeof value === 'object' && value !== null && info.fields) {
const fields = info.fields || []
const fieldNames = fields.map((field) => structFieldRuntimeKey(field))
const valueFields = Object.keys(value)
const valueFields = ownKeys(value)

const fieldsExist = fieldNames.every((field) => field in value)
const sameFieldCount = valueFields.length === fieldNames.length
Expand Down Expand Up @@ -1440,8 +1440,22 @@ function typedElementMatches(

// structValueMarker is the property that marks a struct instance as a value,
// not a pointer. The constructors of generated struct classes set it
// themselves.
export const structValueMarker = Symbol('structValue')
// themselves, by this name. It is a string key, which JavaScriptCore stores
// as fast as a field, where a symbol key on a new object made every struct
// value about ten times slower to make; ownKeys leaves it out of the keys of
// an object.
export const structValueMarker = '__goStructValue'

// ownKeys returns the own enumerable keys of value, as Object.keys does,
// without the struct value marker.
export function ownKeys(value: object): string[] {
const keys = Object.keys(value)
const i = keys.lastIndexOf(structValueMarker)
if (i >= 0) {
keys.splice(i, 1)
}
return keys
}

// markAsStructValue Mark a struct instance as representing a value (not pointer).
export function markAsStructValue<T>(value: T): T {
Expand Down Expand Up @@ -1737,7 +1751,7 @@ function forwardStructFields(view: object, read: () => unknown): void {
if (typeof current !== 'object' || current === null) {
return
}
for (const key of Object.keys(current)) {
for (const key of ownKeys(current)) {
Object.defineProperty(view, key, {
configurable: true,
enumerable: true,
Expand Down
5 changes: 3 additions & 2 deletions gs/encoding/json/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -738,7 +738,8 @@ function encodeJSON(v: unknown): string {
}
if (t === 'object') {
const parts: string[] = []
for (const [key, value] of Object.entries(v as Record<string, unknown>)) {
for (const key of $.ownKeys(v as object)) {
const value = (v as Record<string, unknown>)[key]
parts.push(JSON.stringify(key) + ':' + encodeJSON(value))
}
return '{' + parts.join(',') + '}'
Expand Down Expand Up @@ -2920,7 +2921,7 @@ function structFields(value: Record<string, unknown>): fieldMetadata[] {
if (fields.length !== 0) {
return fields
}
return Object.keys(value).map((key) => ({ key, name: key }))
return $.ownKeys(value).map((key) => ({ key, name: key }))
}

// anonymousStructValueFields returns the fields of an anonymous struct
Expand Down
10 changes: 5 additions & 5 deletions gs/fmt/fmt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,7 @@ function defaultFormatMaybe(
const fields: $.StructFieldInfo[] =
info?.kind === $.TypeKind.Struct ?
info.fields
: Object.keys(value).map((name) => ({
: $.ownKeys(value).map((name) => ({
name,
key: name,
type: $.basicType('unknown'),
Expand Down Expand Up @@ -685,15 +685,15 @@ function defaultFormatMaybe(
(value as any).constructor?.name &&
(value as any).constructor.name !== 'Object'
) {
const parts = Object.entries(value as Record<string, any>).map(
([k, v]) => {
const parts = $.ownKeys(value)
.map((key) => [key, value[key]] as const)
.map(([k, v]) => {
const formatted = defaultFormatMaybe(v, flags, depth + 1)
if (isPromiseLike(formatted)) {
return formatted.then((resolved) => `${k}:${resolved}`)
}
return `${k}:${formatted}`
},
)
})
return joinMaybe(parts, ' ', '{', '}')
}
try {
Expand Down
2 changes: 1 addition & 1 deletion gs/hash/maphash/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ function writeValue(h: Hash, v: unknown): void {
const fields = $.isStructObject(v) ? v : undefined
if ($.isMarkedAsStructValue(v) && fields) {
h.WriteByte(7)
for (const key of Object.keys(fields)) {
for (const key of $.ownKeys(fields)) {
writeValue(h, fields[key])
}
return
Expand Down
11 changes: 8 additions & 3 deletions gs/reflect/deepequal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,12 @@
// values that have been compared before, it treats the values as
// equal rather than examining the values to which they point.
// This ensures that DeepEqual terminates.
import { isNamedValueBox, isStructObject, isVarRef } from '../builtin/index.js'
import {
isNamedValueBox,
isStructObject,
isVarRef,
ownKeys,
} from '../builtin/index.js'
import { ReflectValue } from './types.js'

export function DeepEqual(
Expand Down Expand Up @@ -163,8 +168,8 @@ export function DeepEqual(
return false
}

const keysX = Object.keys(x)
const keysY = Object.keys(y)
const keysX = ownKeys(x)
const keysY = ownKeys(y)
if (keysX.length !== keysY.length) {
return false
}
Expand Down
2 changes: 1 addition & 1 deletion gs/reflect/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -746,7 +746,7 @@ const invalidTypeInstance = new InvalidTypeClass()
// without listing them all as Object.keys would.
function isEmptyObject(value: object): boolean {
for (const key in value) {
if (globalThis.Object.hasOwn(value, key)) {
if (key !== $.structValueMarker && globalThis.Object.hasOwn(value, key)) {
return false
}
}
Expand Down
6 changes: 5 additions & 1 deletion gs/unique/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,11 @@ function normalize(
if ($.isStructObject(value)) {
return {
$type: value.constructor.name,
$fields: normalize({ ...value }, seen, nextID),
$fields: normalize(
Object.fromEntries($.ownKeys(value).map((k) => [k, value[k]])),
seen,
nextID,
),
}
}
const entries = Object.entries(value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export class Col {
constructor(__goscriptValue?: boolean, Name$?: string, Default$?: $.VarRef<number> | null) {
this.Name = Name$ ?? ""
this.Default = Default$ ?? (null! as $.VarRef<number> | null)
;(this as any)[$.structValueMarker] = __goscriptValue === true
;(this as any).__goStructValue = __goscriptValue === true
}

public clone(): Col {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export class Format {
constructor(__goscriptValue?: boolean, Name$?: string, Ext$?: $.Slice<string>) {
this.Name = Name$ ?? ""
this.Ext = Ext$ ?? (null! as $.Slice<string>)
;(this as any)[$.structValueMarker] = __goscriptValue === true
;(this as any).__goStructValue = __goscriptValue === true
}

public clone(): Format {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export class Buffer {

constructor(__goscriptValue?: boolean, data$?: $.Slice<number>) {
this.data = data$ ?? (null! as $.Slice<number>)
;(this as any)[$.structValueMarker] = __goscriptValue === true
;(this as any).__goStructValue = __goscriptValue === true
}

public clone(): Buffer {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export class entry {

constructor(__goscriptValue?: boolean, value$?: number) {
this.value = value$ ?? 0
;(this as any)[$.structValueMarker] = __goscriptValue === true
;(this as any).__goStructValue = __goscriptValue === true
}

public clone(): entry {
Expand Down Expand Up @@ -40,7 +40,7 @@ export class config {

constructor(__goscriptValue?: boolean, core$?: {"bare": boolean, "name": string}) {
this.core = core$ ?? $.anonymousStructValue({"bare": false, "name": ""}, { kind: $.TypeKind.Struct, methods: [], fields: [/* @__PURE__ */ $.structField("bare", /* @__PURE__ */ $.basicType("bool"), [0], 0, false, { pkgPath: "github.com/s4wave/goscript/tests/tests/anonymous_struct_field_address" }), /* @__PURE__ */ $.structField("name", /* @__PURE__ */ $.basicType("string"), [1], 8, false, { pkgPath: "github.com/s4wave/goscript/tests/tests/anonymous_struct_field_address" })] })
;(this as any)[$.structValueMarker] = __goscriptValue === true
;(this as any).__goStructValue = __goscriptValue === true
}

public clone(): config {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export class namedItem {
constructor(__goscriptValue?: boolean, key$?: string, data$?: $.Slice<number>) {
this.key = key$ ?? ""
this.data = data$ ?? (null! as $.Slice<number>)
;(this as any)[$.structValueMarker] = __goscriptValue === true
;(this as any).__goStructValue = __goscriptValue === true
}

public clone(): namedItem {
Expand Down
6 changes: 3 additions & 3 deletions tests/tests/append_field_pointers/append_field_pointers.gs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export class T {

constructor(__goscriptValue?: boolean, N$?: number) {
this.N = N$ ?? 0
;(this as any)[$.structValueMarker] = __goscriptValue === true
;(this as any).__goStructValue = __goscriptValue === true
}

public clone(): T {
Expand Down Expand Up @@ -58,7 +58,7 @@ export class Outer {
constructor(__goscriptValue?: boolean, T$?: T, X$?: number) {
this.T = T$ ?? new T(true)
this.X = X$ ?? 0
;(this as any)[$.structValueMarker] = __goscriptValue === true
;(this as any).__goStructValue = __goscriptValue === true
}

public clone(): Outer {
Expand Down Expand Up @@ -96,7 +96,7 @@ export class Pair {
constructor(__goscriptValue?: boolean, A$?: T, B$?: T) {
this.A = A$ ?? new T(true)
this.B = B$ ?? new T(true)
;(this as any)[$.structValueMarker] = __goscriptValue === true
;(this as any).__goStructValue = __goscriptValue === true
}

public clone(): Pair {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export class pos {
constructor(__goscriptValue?: boolean, Line$?: number, Char$?: number) {
this.Line = Line$ ?? 0
this.Char = Char$ ?? 0
;(this as any)[$.structValueMarker] = __goscriptValue === true
;(this as any).__goStructValue = __goscriptValue === true
}

public clone(): pos {
Expand Down
2 changes: 1 addition & 1 deletion tests/tests/append_value_copy/append_value_copy.gs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export class cell {

constructor(__goscriptValue?: boolean, width$?: number) {
this.width = width$ ?? 0
;(this as any)[$.structValueMarker] = __goscriptValue === true
;(this as any).__goStructValue = __goscriptValue === true
}

public clone(): cell {
Expand Down
4 changes: 2 additions & 2 deletions tests/tests/array_assign_in_place/array_assign_in_place.gs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export class T {

constructor(__goscriptValue?: boolean, v$?: number) {
this.v = v$ ?? 0
;(this as any)[$.structValueMarker] = __goscriptValue === true
;(this as any).__goStructValue = __goscriptValue === true
}

public clone(): T {
Expand Down Expand Up @@ -42,7 +42,7 @@ export class S {
constructor(__goscriptValue?: boolean, A$?: T[], N$?: number[]) {
this.A = A$ ?? $.arrayValue(globalThis.Array.from({ length: 2 }, () => new T(true)), /* @__PURE__ */ $.arrayType("main.T", 2))
this.N = N$ ?? $.arrayValue(globalThis.Array.from({ length: 3 }, () => 0))
;(this as any)[$.structValueMarker] = __goscriptValue === true
;(this as any).__goStructValue = __goscriptValue === true
}

public clone(): S {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export class bitset {

constructor(__goscriptValue?: boolean, bits$?: Uint8Array) {
this.bits = bits$ ?? $.arrayValue(new Uint8Array(4))
;(this as any)[$.structValueMarker] = __goscriptValue === true
;(this as any).__goStructValue = __goscriptValue === true
}

public clone(): bitset {
Expand Down
4 changes: 2 additions & 2 deletions tests/tests/array_index_variable/array_index_variable.gs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export class point {
constructor(__goscriptValue?: boolean, x$?: number, y$?: number) {
this.x = x$ ?? 0
this.y = y$ ?? 0
;(this as any)[$.structValueMarker] = __goscriptValue === true
;(this as any).__goStructValue = __goscriptValue === true
}

public clone(): point {
Expand Down Expand Up @@ -41,7 +41,7 @@ export class holder {
constructor(__goscriptValue?: boolean, points$?: point[], names$?: string[]) {
this.points = points$ ?? $.arrayValue(globalThis.Array.from({ length: 2 }, () => new point(true)), /* @__PURE__ */ $.arrayType("main.point", 2))
this.names = names$ ?? $.arrayValue(globalThis.Array.from({ length: 3 }, () => ""))
;(this as any)[$.structValueMarker] = __goscriptValue === true
;(this as any).__goStructValue = __goscriptValue === true
}

public clone(): holder {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export class node {
constructor(__goscriptValue?: boolean, sub$?: $.Slice<node | $.VarRef<node> | null>, sub0$?: (node | $.VarRef<node> | null)[]) {
this.sub = sub$ ?? (null! as $.Slice<node | $.VarRef<node> | null>)
this.sub0 = sub0$ ?? $.arrayValue(globalThis.Array.from({ length: 1 }, () => null))
;(this as any)[$.structValueMarker] = __goscriptValue === true
;(this as any).__goStructValue = __goscriptValue === true
}

public clone(): node {
Expand Down
4 changes: 2 additions & 2 deletions tests/tests/array_pointer_ops/array_pointer_ops.gs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export class item {

constructor(__goscriptValue?: boolean, value$?: number) {
this.value = value$ ?? 0
;(this as any)[$.structValueMarker] = __goscriptValue === true
;(this as any).__goStructValue = __goscriptValue === true
}

public clone(): item {
Expand Down Expand Up @@ -37,7 +37,7 @@ export class arrays {
constructor(__goscriptValue?: boolean, slices$?: $.Slice<number>[], items$?: item[]) {
this.slices = slices$ ?? $.arrayValue(globalThis.Array.from({ length: 1 }, () => null))
this.items = items$ ?? $.arrayValue(globalThis.Array.from({ length: 1 }, () => new item(true)), /* @__PURE__ */ $.arrayType("main.item", 1))
;(this as any)[$.structValueMarker] = __goscriptValue === true
;(this as any).__goStructValue = __goscriptValue === true
}

public clone(): arrays {
Expand Down
Loading
Loading