@geastack/compiler 1.0.16 → 1.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/conversion/nodes.d.ts +31 -0
- package/dist/conversion/nodes.js +46 -2
- package/dist/conversion/record-view.d.ts +24 -0
- package/dist/conversion/record-view.js +54 -0
- package/dist/ir/certify.js +23 -1
- package/dist/ir/lower-operands.d.ts +10 -1
- package/dist/ir/lower-operands.js +58 -2
- package/dist/ir/lower.js +12 -3
- package/dist/ir/reflection-demand.js +5 -0
- package/dist/plugins/apple/host.d.ts +2 -0
- package/dist/plugins/apple/host.js +13 -6
- package/dist/plugins/apple/jsx.js +20 -2
- package/dist/plugins/gea/host.d.ts +2 -0
- package/dist/plugins/gea/host.js +11 -4
- package/dist/plugins/host-package.d.ts +37 -0
- package/dist/plugins/host-package.js +28 -0
- package/dist/plugins/load.js +15 -0
- package/dist/plugins/webgl/host.d.ts +9 -3
- package/dist/plugins/webgl/host.js +15 -6
- package/dist/plugins/webgl/plugin.js +4 -1
- package/dist/representation/embedded-carriers.d.ts +22 -0
- package/dist/representation/embedded-carriers.js +105 -0
- package/dist/semantics/model/operands.d.ts +17 -0
- package/dist/semantics/model/operations.d.ts +28 -1
- package/dist/semantics/normalize/producers/allocations.js +2 -0
- package/dist/semantics/normalize/producers/bindings.js +5 -1
- package/dist/semantics/normalize/producers/boundary.d.ts +1 -0
- package/dist/semantics/normalize/producers/boundary.js +6 -2
- package/dist/semantics/normalize/producers/class-lifecycle.js +3 -0
- package/dist/semantics/normalize/producers/control.js +2 -2
- package/dist/semantics/normalize/producers/erasure.d.ts +7 -0
- package/dist/semantics/normalize/producers/erasure.js +19 -0
- package/dist/semantics/normalize/producers/exact-arms.d.ts +12 -0
- package/dist/semantics/normalize/producers/exact-arms.js +12 -0
- package/dist/semantics/normalize/producers/invocations.js +33 -1
- package/dist/targets/cpp/class-properties/emit-class-properties.js +4 -4
- package/dist/targets/cpp/conversions.js +63 -1
- package/dist/targets/cpp/emit-callable.js +5 -5
- package/dist/targets/cpp/emit-context.d.ts +38 -1
- package/dist/targets/cpp/emit-context.js +28 -2
- package/dist/targets/cpp/emit-narrowing.d.ts +1 -1
- package/dist/targets/cpp/emit-narrowing.js +47 -1
- package/dist/targets/cpp/emit-record-view.js +31 -0
- package/dist/targets/cpp/emit.d.ts +3 -3
- package/dist/targets/cpp/emit.js +11 -5
- package/dist/targets/cpp/translation-unit.js +24 -15
- package/package.json +1 -1
- package/src/targets/cpp/runtime/gea_runtime.h +123 -44
|
@@ -11,7 +11,7 @@ import { cppTypeOf } from './types.js';
|
|
|
11
11
|
import { nativeSumWidenable } from '../../conversion/native-sum.js';
|
|
12
12
|
import { nativeSelectionRecipeOf, nativeTotalSelectionRecipeOf } from '../../conversion/native-selection.js';
|
|
13
13
|
import { nativeClassReferenceIdentityOf } from '../../conversion/native-class-reference.js';
|
|
14
|
-
import { ownedRecordMaterializationPlan, recordViewUsesOnlyDirectFields } from '../../conversion/record-view.js';
|
|
14
|
+
import { isRecordViewTarget, ownedRecordMaterializationPlan, recordViewDispatchesArms, recordViewUsesOnlyDirectFields } from '../../conversion/record-view.js';
|
|
15
15
|
import { cppStringObjectNativeType } from './regexp-types.js';
|
|
16
16
|
import { viewPlanFor } from './emit-record-view.js';
|
|
17
17
|
/** Called only after the native converting-constructor predicate has selected this pair. */
|
|
@@ -446,6 +446,37 @@ const dynamicCallablePair = (target) => {
|
|
|
446
446
|
materializer: { id: 'gea::detail::DynamicCarrier::in', domain, allocates: true }
|
|
447
447
|
};
|
|
448
448
|
};
|
|
449
|
+
/**
|
|
450
|
+
* A shared class handle among a sum's arms, at a record-shaped target it
|
|
451
|
+
* reaches by neither the chain nor the structural view.
|
|
452
|
+
*
|
|
453
|
+
* Nothing in the program proves that arm dead: an interface-typed slot holds
|
|
454
|
+
* whatever object the program put there (`semantics/interface-implementors.ts`),
|
|
455
|
+
* and a class this cannot view as the record is still a value the slot can be
|
|
456
|
+
* handed -- skytail's `const ctx: AudioContextLike | null = Ctor ? new Ctor()
|
|
457
|
+
* : createNativeAudioContext()`, whose `NativeAudioContext` carries class-typed
|
|
458
|
+
* fields and a promise the view cannot rebuild. Selecting the record arm
|
|
459
|
+
* there read the class instance's bytes as the record and crashed at launch,
|
|
460
|
+
* certified. A record arm beside the exact one is the opposite case: a record
|
|
461
|
+
* the view cannot rebuild as the target is not assignable to it either, so the
|
|
462
|
+
* checker's own narrowing is what put the pair here, and the selection stands.
|
|
463
|
+
*
|
|
464
|
+
* Asked by every table that could answer the pair -- `narrowing` and
|
|
465
|
+
* `staticRecipe` -- so the census refuses it outright rather than one table
|
|
466
|
+
* declining and the next selecting.
|
|
467
|
+
*/
|
|
468
|
+
const classArmWithoutHome = (layouts, source, target) => {
|
|
469
|
+
const union = source.kind === 'optional' ? source.payload : source;
|
|
470
|
+
const selected = target.kind === 'optional' ? target.payload : target;
|
|
471
|
+
if (union.kind !== 'tagged-union' || !isRecordViewTarget(selected))
|
|
472
|
+
return false;
|
|
473
|
+
const key = representationKey(selected);
|
|
474
|
+
return union.arms.some(({ value }) => value.kind === 'class-ref' &&
|
|
475
|
+
value.ownership === 'shared-refcount' &&
|
|
476
|
+
representationKey(value) !== key &&
|
|
477
|
+
conversionRecipeOf(value, selected)?.renders !== true &&
|
|
478
|
+
viewPlanFor(layouts, value, selected) === null);
|
|
479
|
+
};
|
|
449
480
|
export const createCppConversionRegistry = (layouts = defaultRecordLayoutPolicy) => {
|
|
450
481
|
// A sum narrowing's contract is composed from the contracts this same
|
|
451
482
|
// registry states for its leaf pairs (`native-narrowing-transport.ts`), so
|
|
@@ -791,6 +822,8 @@ const cppConversionTables = (layouts, nativeNarrowing, nativeWrappedPayload) =>
|
|
|
791
822
|
// can see, and whose lifetime the program never stated.
|
|
792
823
|
if ('ownership' in target && target.ownership === 'borrowed')
|
|
793
824
|
return null;
|
|
825
|
+
if (classArmWithoutHome(layouts, source, target))
|
|
826
|
+
return null;
|
|
794
827
|
const setPair = genericFunctionSetPair(source, target);
|
|
795
828
|
if (setPair)
|
|
796
829
|
return setPair;
|
|
@@ -949,6 +982,18 @@ const cppConversionTables = (layouts, nativeNarrowing, nativeWrappedPayload) =>
|
|
|
949
982
|
const selected = source.kind === 'optional' && target.kind === 'optional' ? target.payload : target;
|
|
950
983
|
if (union.kind !== 'tagged-union')
|
|
951
984
|
return null;
|
|
985
|
+
// A record-shaped target some OTHER arm reaches only through the
|
|
986
|
+
// structural view is not a selection: that arm is live at a store, and a
|
|
987
|
+
// selection would read it as the exact arm. The view's `dispatch` plan
|
|
988
|
+
// (`conversion/record-view.ts`) is the conversion, installed by
|
|
989
|
+
// `staticRecipe` below once this table declines -- the same plan
|
|
990
|
+
// `emit-narrowing.ts`'s `narrowedLoadText` defers to, asked here so the
|
|
991
|
+
// admission and the render agree.
|
|
992
|
+
if (isRecordViewTarget(selected)) {
|
|
993
|
+
const view = viewPlanFor(layouts, source, target);
|
|
994
|
+
if (view !== null && recordViewDispatchesArms(view))
|
|
995
|
+
return null;
|
|
996
|
+
}
|
|
952
997
|
const targetKey = representationKey(selected);
|
|
953
998
|
// A SUB-union target: `selected` is still a tagged union, just missing one
|
|
954
999
|
// or more of `union`'s arms -- `h instanceof Headers`'s `else` branch
|
|
@@ -1972,6 +2017,22 @@ const cppConversionTables = (layouts, nativeNarrowing, nativeWrappedPayload) =>
|
|
|
1972
2017
|
staticRecipe: (source, target) => {
|
|
1973
2018
|
if (!isSpellable(source) || !isSpellable(target))
|
|
1974
2019
|
return null;
|
|
2020
|
+
if (classArmWithoutHome(layouts, source, target))
|
|
2021
|
+
return null;
|
|
2022
|
+
// A sum some arm of which reaches the record-shaped target only through
|
|
2023
|
+
// the structural view is that view's `dispatch` plan, asked BEFORE the
|
|
2024
|
+
// chain: the chain answers the pair too, by selecting the exact arm, and
|
|
2025
|
+
// that selection is the narrowing this table's `narrowing` entry has
|
|
2026
|
+
// already declined for the pair (`recordViewDispatchesArms`). The
|
|
2027
|
+
// printer renders in the same order (`emit-narrowing.ts`'s `recipeText`).
|
|
2028
|
+
const dispatching = viewPlanFor(layouts, source, target);
|
|
2029
|
+
if (dispatching !== null && recordViewDispatchesArms(dispatching))
|
|
2030
|
+
return {
|
|
2031
|
+
id: 'view:structural-record',
|
|
2032
|
+
domain: 'static:structural-record-view',
|
|
2033
|
+
allocates: true,
|
|
2034
|
+
...(recordViewUsesOnlyDirectFields(dispatching) ? { nativeFieldProtocol: 'unused' } : {})
|
|
2035
|
+
};
|
|
1975
2036
|
const recipe = conversionRecipeOf(source, target);
|
|
1976
2037
|
if (recipe !== null && recipe.renders) {
|
|
1977
2038
|
// `unreachable-value` spells either `unreachableValue<T>()` (a throw that
|
|
@@ -2022,6 +2083,7 @@ const allocatingRecipes = new Set([
|
|
|
2022
2083
|
'record-recast',
|
|
2023
2084
|
'optional-record-recast',
|
|
2024
2085
|
'record-to-dictionary',
|
|
2086
|
+
'dictionary-to-record',
|
|
2025
2087
|
'record-to-array',
|
|
2026
2088
|
'promise-payload'
|
|
2027
2089
|
]);
|
|
@@ -10,7 +10,7 @@ import { alignedValueText, bindsReceiver, callableObjectAbi, movedValueText, unb
|
|
|
10
10
|
import { memberAccessOperator, reactiveRevisionText } from './emit-carrier-members.js';
|
|
11
11
|
import { structuralRecordViewText } from './emit-record-view.js';
|
|
12
12
|
import { emitDateConstruct, isDateCarrier } from './prototype/emit-prototype-date.js';
|
|
13
|
-
import { unwrapPresentValue, bindingReference, cppConstructedThunkName, cppConstructThunkName, cppEnvironmentStructName, cppReceiverName, cppThunkName, createCppEmitBlockedError, declareCell, defineValue, defineValueAlias, isCppEmitBlockedError, isIntegerStorageValue, operandText, paddedArguments, captureFieldText } from './emit-context.js';
|
|
13
|
+
import { unwrapPresentValue, bindingReference, cppConstructedThunkName, cppConstructThunkName, cppEnvironmentStructName, cppReceiverName, cppThunkEntryText, cppThunkName, createCppEmitBlockedError, declareCell, defineValue, defineValueAlias, isCppEmitBlockedError, isIntegerStorageValue, operandText, paddedArguments, captureFieldText } from './emit-context.js';
|
|
14
14
|
import { classMemberOf, classStaticMemberOf, cppFieldInitializerStatements, lazyArrowFieldPlanOf } from './class-layout.js';
|
|
15
15
|
import { declaredRecordFieldOf } from './records.js';
|
|
16
16
|
import { hostArityCallLines, hostResultText } from './host/emit-host-arity.js';
|
|
@@ -1501,8 +1501,8 @@ export const emitAllocateCallable = (ctx, lines, operation) => {
|
|
|
1501
1501
|
// Brace-initializing this carrier with two values is what used to leave every
|
|
1502
1502
|
// pre-`class` constructor function in three.js's renderer unclaimed.
|
|
1503
1503
|
const pointers = payloadCarrier.kind === 'function-and-constructor'
|
|
1504
|
-
?
|
|
1505
|
-
:
|
|
1504
|
+
? `${cppThunkEntryText(ctx, operation.functionId)}, &${cppConstructedThunkName(operation.functionId)}`
|
|
1505
|
+
: cppThunkEntryText(ctx, operation.functionId);
|
|
1506
1506
|
// Tagged by the SOURCE DECLARATION, not by this copy's thunk. Two
|
|
1507
1507
|
// instantiations of one generic function are two thunks and one JavaScript
|
|
1508
1508
|
// function object; the runtime's own `CallableDeclarationTag` comment says
|
|
@@ -1551,11 +1551,11 @@ export const emitAllocateCallable = (ctx, lines, operation) => {
|
|
|
1551
1551
|
return `gea::Value::boxCallable<${abi.restFrom}>(${callable})`;
|
|
1552
1552
|
};
|
|
1553
1553
|
if (admission.kind === 'none') {
|
|
1554
|
-
lines.push(`${name} = gea::host::installOrdinaryConstructorPrototype(${boxText(`${callableType}{
|
|
1554
|
+
lines.push(`${name} = gea::host::installOrdinaryConstructorPrototype(${boxText(`${callableType}{${cppThunkEntryText(ctx, operation.functionId)}, nullptr}`)});`);
|
|
1555
1555
|
return;
|
|
1556
1556
|
}
|
|
1557
1557
|
const dynamicEnvironment = packedEnvironmentText(ctx, lines, operation.functionId, admission);
|
|
1558
|
-
lines.push(`${name} = gea::host::installOrdinaryConstructorPrototype(${boxText(`${callableType}{
|
|
1558
|
+
lines.push(`${name} = gea::host::installOrdinaryConstructorPrototype(${boxText(`${callableType}{${cppThunkEntryText(ctx, operation.functionId)}, ${dynamicEnvironment}}`)});`);
|
|
1559
1559
|
return;
|
|
1560
1560
|
}
|
|
1561
1561
|
if (admission.kind === 'none') {
|
|
@@ -678,6 +678,13 @@ export interface EmitContext {
|
|
|
678
678
|
readonly generatorBody: boolean;
|
|
679
679
|
/** The conventions of emitted bodies, shared with their signature renderer. */
|
|
680
680
|
readonly abiOfCallable: (callable: FunctionId) => CallableAbi | null;
|
|
681
|
+
/**
|
|
682
|
+
* The `name`/`length`/source text each function object must be able to
|
|
683
|
+
* answer, keyed by the body -- empty when the program never reads any of
|
|
684
|
+
* them (`translation-unit.ts`'s `preserveFunctionFacts` census). Read by
|
|
685
|
+
* `cppThunkEntryText`, at the sites that mint a function object.
|
|
686
|
+
*/
|
|
687
|
+
readonly functionFacts: ReadonlyMap<FunctionId, CallableFactsSpelling>;
|
|
681
688
|
/** The execution context whose frame this body is, so a cell owned elsewhere is recognisable as one. */
|
|
682
689
|
readonly owner: FunctionId | RegionId;
|
|
683
690
|
/** See `emit-narrowing.ts`'s `PrinterDrift`; one program-wide list, shared by every body's context. */
|
|
@@ -1622,7 +1629,7 @@ export declare const directCalleesOf: (body: IrBody, abiOfCallable: (callable: F
|
|
|
1622
1629
|
readonly directCallees: ReadonlyMap<IrValueId, string>;
|
|
1623
1630
|
readonly directCalleeAbis: ReadonlyMap<IrValueId, CallableAbi>;
|
|
1624
1631
|
};
|
|
1625
|
-
export declare const createEmitContext: (abi: CallableAbi | null, owner: FunctionId | RegionId, placements: ReadonlyMap<DeclarationId, BindingPlacement>, classes: ReadonlyMap<DeclarationId, ClassLayout>, hosts: HostSpellings, deriver: RepresentationDeriver, bodyFacts: EmitBodyFacts, wellKnownSymbols?: ReadonlyMap<DeclarationId, string>, captures?: CaptureIndex, symbolKeys?: Map<string, string>, templateObjects?: Map<string, TemplateObjectDefinition>, directCallableBindings?: ReadonlyMap<DeclarationId, FunctionId>, virtualDispatch?: ReadonlyMap<string, CallableAbi>, narrowedFormals?: ReadonlySet<number>, repeatedConstructors?: ReadonlyMap<DeclarationId, DeclarationId>, dyingArguments?: ReadonlySet<IrValueId>, instantiation?: InstantiationFacts, abiOfCallable?: (callable: FunctionId) => CallableAbi | null, hostMethodAliases?: ReadonlyMap<DeclarationId, HostMethodAlias>, callableMemberCandidates?: ReadonlyMap<string, FunctionId>, borrowableMemberBodies?: ReadonlySet<FunctionId>, stableBorrowEntries?: ReadonlyMap<string, StableBorrowEntry>, printerDrift?: PrinterDrift[], conversions?: ConversionCensus | null, nativeSelections?: ReadonlyMap<string, NativeSelectionHelper> | undefined, callableIdentityDemand?: CallableIdentityDemand, nativeIntegrityRestricted?: boolean, fixedFieldStateConstant?: boolean) => {
|
|
1632
|
+
export declare const createEmitContext: (abi: CallableAbi | null, owner: FunctionId | RegionId, placements: ReadonlyMap<DeclarationId, BindingPlacement>, classes: ReadonlyMap<DeclarationId, ClassLayout>, hosts: HostSpellings, deriver: RepresentationDeriver, bodyFacts: EmitBodyFacts, wellKnownSymbols?: ReadonlyMap<DeclarationId, string>, captures?: CaptureIndex, symbolKeys?: Map<string, string>, templateObjects?: Map<string, TemplateObjectDefinition>, directCallableBindings?: ReadonlyMap<DeclarationId, FunctionId>, virtualDispatch?: ReadonlyMap<string, CallableAbi>, narrowedFormals?: ReadonlySet<number>, repeatedConstructors?: ReadonlyMap<DeclarationId, DeclarationId>, dyingArguments?: ReadonlySet<IrValueId>, instantiation?: InstantiationFacts, abiOfCallable?: (callable: FunctionId) => CallableAbi | null, functionFacts?: ReadonlyMap<FunctionId, CallableFactsSpelling>, hostMethodAliases?: ReadonlyMap<DeclarationId, HostMethodAlias>, callableMemberCandidates?: ReadonlyMap<string, FunctionId>, borrowableMemberBodies?: ReadonlySet<FunctionId>, stableBorrowEntries?: ReadonlyMap<string, StableBorrowEntry>, printerDrift?: PrinterDrift[], conversions?: ConversionCensus | null, nativeSelections?: ReadonlyMap<string, NativeSelectionHelper> | undefined, callableIdentityDemand?: CallableIdentityDemand, nativeIntegrityRestricted?: boolean, fixedFieldStateConstant?: boolean) => {
|
|
1626
1633
|
readonly ctx: EmitContext;
|
|
1627
1634
|
readonly prepass: EmitBodyPrepassFacts;
|
|
1628
1635
|
};
|
|
@@ -1739,6 +1746,36 @@ export declare const cppFormalName: (ordinal: number) => string;
|
|
|
1739
1746
|
export declare const cppReceiverName = "gea_this";
|
|
1740
1747
|
/** The thunk that adapts one function body to the environment-passing invoke pointer a callable carrier holds. */
|
|
1741
1748
|
export declare const cppThunkName: (functionId: string) => string;
|
|
1749
|
+
/**
|
|
1750
|
+
* What one function object answers for `name`, `length` and
|
|
1751
|
+
* `Function.prototype.toString`, already spelled for the registration call:
|
|
1752
|
+
* `abiType` is `cppAbiType` of the thunk's own convention, which the
|
|
1753
|
+
* registry's `Invoke` template parameter must match exactly.
|
|
1754
|
+
*/
|
|
1755
|
+
export interface CallableFactsSpelling {
|
|
1756
|
+
readonly abiType: string;
|
|
1757
|
+
readonly name: string;
|
|
1758
|
+
readonly length: number;
|
|
1759
|
+
readonly source: string;
|
|
1760
|
+
}
|
|
1761
|
+
/**
|
|
1762
|
+
* The invoke pointer a minting site stores in a callable carrier: `&thunk`,
|
|
1763
|
+
* or -- when the program reads function facts -- `&thunk` handed back by
|
|
1764
|
+
* `gea::CallableObject<Abi>::entryWithFacts<&thunk>(name, length, text)`,
|
|
1765
|
+
* which registers the facts the first time any site mints this function.
|
|
1766
|
+
*
|
|
1767
|
+
* Registration lives HERE, at the mint, and not beside the thunk, because a
|
|
1768
|
+
* namespace-scope `static const bool ... = registerSource<&thunk>(...)` is a
|
|
1769
|
+
* static initializer that takes the thunk's address, and a static initializer
|
|
1770
|
+
* with a side effect is a root the linker may not drop. Every emitted
|
|
1771
|
+
* function, and its source text, then survived `--gc-sections` and LTO
|
|
1772
|
+
* whether or not anything reached it: a raw HTTP server whose `EventEmitter`
|
|
1773
|
+
* boxed one listener kept all 384 of its functions. A mint site is reachable
|
|
1774
|
+
* exactly when a function object can exist, and only an existing function
|
|
1775
|
+
* object can be asked for its facts, so registering there preserves every
|
|
1776
|
+
* observable answer and pins nothing else.
|
|
1777
|
+
*/
|
|
1778
|
+
export declare const cppThunkEntryText: (ctx: EmitContext, functionId: FunctionId) => string;
|
|
1742
1779
|
/** The thunk that adapts one class's construct function to the environment-passing construct pointer a constructor carrier holds. */
|
|
1743
1780
|
export declare const cppConstructThunkName: (declaration: DeclarationId) => string;
|
|
1744
1781
|
/**
|
|
@@ -4,7 +4,7 @@ import { noInstantiationFacts } from '../../ir/instantiation.js';
|
|
|
4
4
|
import { observesEveryCallableIdentity } from '../../ir/callable-identity-demand.js';
|
|
5
5
|
import { hostCallName, hostMemberOf, statedHostIntrinsicLength } from './host/host-members.js';
|
|
6
6
|
import { representationKey } from '../../representation/model.js';
|
|
7
|
-
import { cppBodyName, cppConstructName, cppNarrowedIntegerType, cppStringLiteral, cppTypeOf, cppUndefinedIn } from './types.js';
|
|
7
|
+
import { cppBodyName, cppConstructName, cppNarrowedIntegerType, cppStringLiteral, cppStringViewLiteral, cppTypeOf, cppUndefinedIn } from './types.js';
|
|
8
8
|
import { hostBuiltinFunctionIdentityText, hostClassValueText, hostFunctionValueText, hostJsonMemberValueText, hostMemberValueText } from './host/emit-host-value.js';
|
|
9
9
|
import { createConversionNodes } from '../../conversion/nodes.js';
|
|
10
10
|
import { createCppConversionRegistry } from './conversions.js';
|
|
@@ -75,7 +75,7 @@ export const directCalleesOf = (body, abiOfCallable) => {
|
|
|
75
75
|
}
|
|
76
76
|
return { directCallees, directCalleeAbis };
|
|
77
77
|
};
|
|
78
|
-
export const createEmitContext = (abi, owner, placements, classes, hosts, deriver, bodyFacts, wellKnownSymbols = new Map(), captures = emptyCaptureIndex, symbolKeys = new Map(), templateObjects = new Map(), directCallableBindings = new Map(), virtualDispatch = new Map(), narrowedFormals = new Set(), repeatedConstructors = new Map(), dyingArguments = new Set(), instantiation = noInstantiationFacts, abiOfCallable = () => null, hostMethodAliases = new Map(), callableMemberCandidates = new Map(), borrowableMemberBodies = new Set(), stableBorrowEntries = new Map(), printerDrift = [],
|
|
78
|
+
export const createEmitContext = (abi, owner, placements, classes, hosts, deriver, bodyFacts, wellKnownSymbols = new Map(), captures = emptyCaptureIndex, symbolKeys = new Map(), templateObjects = new Map(), directCallableBindings = new Map(), virtualDispatch = new Map(), narrowedFormals = new Set(), repeatedConstructors = new Map(), dyingArguments = new Set(), instantiation = noInstantiationFacts, abiOfCallable = () => null, functionFacts = new Map(), hostMethodAliases = new Map(), callableMemberCandidates = new Map(), borrowableMemberBodies = new Set(), stableBorrowEntries = new Map(), printerDrift = [],
|
|
79
79
|
// A context built without the program's census (the value-contract
|
|
80
80
|
// checks) gets one over the same tables with nothing pre-minted: the
|
|
81
81
|
// registry answers every pair the eager graph would have, one node later.
|
|
@@ -126,6 +126,7 @@ conversions = null, nativeSelections = undefined, callableIdentityDemand = obser
|
|
|
126
126
|
const ctx = {
|
|
127
127
|
abi: effectiveAbiOf(abi, admission),
|
|
128
128
|
abiOfCallable,
|
|
129
|
+
functionFacts,
|
|
129
130
|
owner,
|
|
130
131
|
placements,
|
|
131
132
|
classes,
|
|
@@ -537,6 +538,31 @@ export const cppFormalName = (ordinal) => `gea_arg_${ordinal}`;
|
|
|
537
538
|
export const cppReceiverName = 'gea_this';
|
|
538
539
|
/** The thunk that adapts one function body to the environment-passing invoke pointer a callable carrier holds. */
|
|
539
540
|
export const cppThunkName = (functionId) => `${cppBodyName(functionId)}_thunk`;
|
|
541
|
+
/**
|
|
542
|
+
* The invoke pointer a minting site stores in a callable carrier: `&thunk`,
|
|
543
|
+
* or -- when the program reads function facts -- `&thunk` handed back by
|
|
544
|
+
* `gea::CallableObject<Abi>::entryWithFacts<&thunk>(name, length, text)`,
|
|
545
|
+
* which registers the facts the first time any site mints this function.
|
|
546
|
+
*
|
|
547
|
+
* Registration lives HERE, at the mint, and not beside the thunk, because a
|
|
548
|
+
* namespace-scope `static const bool ... = registerSource<&thunk>(...)` is a
|
|
549
|
+
* static initializer that takes the thunk's address, and a static initializer
|
|
550
|
+
* with a side effect is a root the linker may not drop. Every emitted
|
|
551
|
+
* function, and its source text, then survived `--gc-sections` and LTO
|
|
552
|
+
* whether or not anything reached it: a raw HTTP server whose `EventEmitter`
|
|
553
|
+
* boxed one listener kept all 384 of its functions. A mint site is reachable
|
|
554
|
+
* exactly when a function object can exist, and only an existing function
|
|
555
|
+
* object can be asked for its facts, so registering there preserves every
|
|
556
|
+
* observable answer and pins nothing else.
|
|
557
|
+
*/
|
|
558
|
+
export const cppThunkEntryText = (ctx, functionId) => {
|
|
559
|
+
const thunk = `&${cppThunkName(functionId)}`;
|
|
560
|
+
const facts = ctx.functionFacts.get(functionId);
|
|
561
|
+
if (facts === undefined)
|
|
562
|
+
return thunk;
|
|
563
|
+
return (`gea::CallableObject<${facts.abiType}>::entryWithFacts<${thunk}>(` +
|
|
564
|
+
`${cppStringViewLiteral(facts.name)}, ${facts.length}, ${cppStringViewLiteral(facts.source)})`);
|
|
565
|
+
};
|
|
540
566
|
/** The thunk that adapts one class's construct function to the environment-passing construct pointer a constructor carrier holds. */
|
|
541
567
|
export const cppConstructThunkName = (declaration) => `${cppConstructName(declaration)}_thunk`;
|
|
542
568
|
/**
|
|
@@ -3,7 +3,7 @@ import type { CallableAbi, Representation, TaggedUnionArm } from '../../represen
|
|
|
3
3
|
import type { IrOperand, MergeLiveArmRebuildOperation } from '../../ir/model.js';
|
|
4
4
|
import type { DeclarationId, FunctionId } from '../../identity/ids.js';
|
|
5
5
|
import type { ConversionNode } from '../../conversion/algebra.js';
|
|
6
|
-
import type
|
|
6
|
+
import { type ConversionCensus } from '../../conversion/nodes.js';
|
|
7
7
|
import type { RecordLayoutPolicy } from '../../representation/policies.js';
|
|
8
8
|
import type { ClassLayout } from '../../projection/classes.js';
|
|
9
9
|
import type { CaptureIndex } from './emit-context.js';
|
|
@@ -2,7 +2,9 @@ import { classRefTransportKind, constructorUpcastMember } from './class-ref-tran
|
|
|
2
2
|
import { abiKey, arrayExtensionKey, containsUnresolved, isBooleanShapedMergeTarget, isCanonicalNumberPropertyKeyText, representationKey, walkRepresentation, carriesUndefined } from '../../representation/model.js';
|
|
3
3
|
import { transferOf } from '../../ir/transfer.js';
|
|
4
4
|
import { coercionText } from './emit-coercion.js';
|
|
5
|
-
import {
|
|
5
|
+
import { EXACT_ARM_MATERIALIZER, exactArmIndexOf } from '../../conversion/nodes.js';
|
|
6
|
+
import { structuralRecordViewText, viewPlanFor } from './emit-record-view.js';
|
|
7
|
+
import { recordViewDispatchesArms } from '../../conversion/record-view.js';
|
|
6
8
|
import { createCppEmitBlockedError, cppConstructThunkName, cppThunkName, defineValue, isCppEmitBlockedError, isDeferredValue, operandText } from './emit-context.js';
|
|
7
9
|
import { booleanTestText } from './emit-presence.js';
|
|
8
10
|
import { recastedRecordToArrayText } from './emit-arrays.js';
|
|
@@ -476,6 +478,16 @@ export const narrowedLoadText = (held, read, text) => {
|
|
|
476
478
|
}
|
|
477
479
|
if (inner.kind !== 'tagged-union')
|
|
478
480
|
return null;
|
|
481
|
+
// Every arm search below SELECTS: it loads the arm the read names and
|
|
482
|
+
// trusts the narrowing that licensed the load to have killed the rest. A
|
|
483
|
+
// store into a declared slot has no such licence -- `const ctx:
|
|
484
|
+
// AudioContextLike | null = Ctor ? new Ctor() : createNativeAudioContext()`
|
|
485
|
+
// converts a `record | class` into the interface's record, and the class
|
|
486
|
+
// arm is as live as the record's. This chain cannot tell the two apart (a
|
|
487
|
+
// class viewed as a record needs the program's layouts), so the census
|
|
488
|
+
// decides it: a pair some arm reaches only through the structural view is
|
|
489
|
+
// `conversion/record-view.ts`'s `dispatch` plan, installed and rendered
|
|
490
|
+
// ahead of this chain (`conversions.ts`'s `staticRecipe`, `recipeText`).
|
|
479
491
|
// A join may preserve another join (or an optional) as one physical arm.
|
|
480
492
|
// Search that nested carrier before trying to compare the outer arm list
|
|
481
493
|
// with the read's list. Otherwise a read of the preserved inner union is
|
|
@@ -1907,6 +1919,14 @@ export const dictionaryCastableToDictionary = (source, target) => {
|
|
|
1907
1919
|
throw error;
|
|
1908
1920
|
}
|
|
1909
1921
|
};
|
|
1922
|
+
const dictionaryToRecordText = (source, target, text) => {
|
|
1923
|
+
if (source.kind !== 'dictionary' || source.key !== 'string' || source.value.kind !== 'dynamic' || source.ownership !== 'shared-refcount')
|
|
1924
|
+
return null;
|
|
1925
|
+
if (target.kind !== 'record' || target.accessors.length !== 0 || target.ownership === 'borrowed')
|
|
1926
|
+
return null;
|
|
1927
|
+
const boxed = dynamicCarrierBoxText(source, text);
|
|
1928
|
+
return boxed === null ? null : unboxedLoadText(target, boxed);
|
|
1929
|
+
};
|
|
1910
1930
|
const recastedDictionaryText = (source, target, text) => {
|
|
1911
1931
|
if (!dictionaryCastableToDictionary(source, target))
|
|
1912
1932
|
return null;
|
|
@@ -3435,6 +3455,18 @@ export const conversionChain = [
|
|
|
3435
3455
|
id: 'dictionary-to-dictionary',
|
|
3436
3456
|
apply: (source, target, text) => source.kind === 'dictionary' && target.kind === 'dictionary' ? claimed(recastedDictionaryText(source, target, text)) : undefined
|
|
3437
3457
|
},
|
|
3458
|
+
// The mirror of `record-to-dictionary` for the one dictionary whose values
|
|
3459
|
+
// are boxed: `globalThis as unknown as { AudioContext?: Ctor }` reads named
|
|
3460
|
+
// fields out of the open string-keyed surface `references.ts` gives
|
|
3461
|
+
// `globalThis`. The closed field list comes from the TARGET, so this is not
|
|
3462
|
+
// the reconstruction `conversion/derive.ts` refuses (recovering a field list
|
|
3463
|
+
// from an open dictionary); it is the dynamic-object product read
|
|
3464
|
+
// (`unboxedLoadText`'s record branch) over that dictionary boxed, which
|
|
3465
|
+
// `gea::detail::dynamicRecordHasField` already reads as a document.
|
|
3466
|
+
{
|
|
3467
|
+
id: 'dictionary-to-record',
|
|
3468
|
+
apply: (source, target, text) => claimed(dictionaryToRecordText(source, target, text))
|
|
3469
|
+
},
|
|
3438
3470
|
// An Array of one element carrier copied into an Array of another, each
|
|
3439
3471
|
// element through its own conversion. A copy, so only for an array that is
|
|
3440
3472
|
// not written through both names afterwards: a matcher table built once
|
|
@@ -3695,6 +3727,11 @@ export const recipeText = (ctx, node, text) => {
|
|
|
3695
3727
|
// `conversions.ts`'s read of a structural value as a class nothing instantiates.
|
|
3696
3728
|
if (node.capability.kind === 'atom' && node.capability.materializer.id === 'gea::host::unreachableValue')
|
|
3697
3729
|
return `((void)(${text}), gea::host::unreachableValue<${cppTypeOf(node.target)}>())`;
|
|
3730
|
+
// The census's exact-arm projection (`nodes.ts`'s `exactArmFor`): the arm
|
|
3731
|
+
// index is a function of the pair, so it is re-derived here rather than
|
|
3732
|
+
// carried on the node.
|
|
3733
|
+
if (node.capability.kind === 'static' && node.capability.materializer.id === EXACT_ARM_MATERIALIZER)
|
|
3734
|
+
return `gea::host::exactArm<${exactArmIndexOf(node.source, node.target)}>(${text})`;
|
|
3698
3735
|
// `conversions.ts`'s read of a class instance's structural view as the class: the boxed origin, narrowed.
|
|
3699
3736
|
if (node.capability.kind === 'atom' && node.capability.materializer.id === 'gea::record::viewOrigin')
|
|
3700
3737
|
return narrowedLoadText({ kind: 'dynamic', reason: 'declared-any-never-narrowed' }, node.target, node.source.kind === 'optional'
|
|
@@ -3706,6 +3743,15 @@ export const recipeText = (ctx, node, text) => {
|
|
|
3706
3743
|
? `${helper.name}(${text})`
|
|
3707
3744
|
: nativeSelectionText(node.capability.materializer.nativeSelection, node.source, node.target, text);
|
|
3708
3745
|
}
|
|
3746
|
+
// A view that DISPATCHES on a sum's live arm is rendered ahead of the
|
|
3747
|
+
// chain: the chain would select the exact arm and trust a narrowing that,
|
|
3748
|
+
// at a store, never happened (`narrowedLoadText`'s arm search says why).
|
|
3749
|
+
// `conversions.ts`'s `staticRecipe` installs the node in the same order.
|
|
3750
|
+
if (node.capability.kind === 'static' && node.capability.materializer.id === 'view:structural-record') {
|
|
3751
|
+
const view = viewPlanFor(ctx.layouts, node.source, node.target);
|
|
3752
|
+
if (view !== null && recordViewDispatchesArms(view))
|
|
3753
|
+
return structuralRecordViewText(ctx, node.source, node.target, text);
|
|
3754
|
+
}
|
|
3709
3755
|
return convertedValueText(node.source, node.target, text) ?? structuralRecordViewText(ctx, node.source, node.target, text);
|
|
3710
3756
|
};
|
|
3711
3757
|
/**
|
|
@@ -266,6 +266,37 @@ const recordViewText = (ctx, plan, text) => {
|
|
|
266
266
|
}
|
|
267
267
|
return recastedUnionFromHomes(plan.source, plan.target, text, homes);
|
|
268
268
|
}
|
|
269
|
+
case 'dispatch': {
|
|
270
|
+
// The same discriminant chain `taggedUnionArmText` spells, with the last
|
|
271
|
+
// arm untested: the plan admitted every arm, so one of them is live.
|
|
272
|
+
// Each home is spelled at the whole target's type so an optional target
|
|
273
|
+
// wraps once per arm and an absent arm is its empty state.
|
|
274
|
+
const targetType = cppTypeOf(plan.target);
|
|
275
|
+
const payload = plan.target.kind === 'optional' ? plan.target.payload : plan.target;
|
|
276
|
+
const homes = [];
|
|
277
|
+
for (const [index, arm] of plan.arms.entries()) {
|
|
278
|
+
const from = plan.source.arms[index];
|
|
279
|
+
if (from === undefined)
|
|
280
|
+
return null;
|
|
281
|
+
const armText = `${text}.get<${index}>()`;
|
|
282
|
+
const rendered = arm.via === 'exact'
|
|
283
|
+
? armText
|
|
284
|
+
: arm.via === 'absent'
|
|
285
|
+
? null
|
|
286
|
+
: arm.via === 'convert'
|
|
287
|
+
? convertedValueText(from.value, payload, armText)
|
|
288
|
+
: recordViewText(ctx, arm.via, armText);
|
|
289
|
+
if (arm.via !== 'absent' && rendered === null)
|
|
290
|
+
return null;
|
|
291
|
+
homes.push(rendered === null ? `${targetType}()` : `${targetType}(${rendered})`);
|
|
292
|
+
}
|
|
293
|
+
let result = homes[homes.length - 1];
|
|
294
|
+
if (result === undefined)
|
|
295
|
+
return null;
|
|
296
|
+
for (let index = homes.length - 2; index >= 0; index--)
|
|
297
|
+
result = `${text}.is<${index}>() ? ${homes[index]} : (${result})`;
|
|
298
|
+
return `(${result})`;
|
|
299
|
+
}
|
|
269
300
|
case 'fields':
|
|
270
301
|
return recordFieldsViewText(ctx, plan, text);
|
|
271
302
|
}
|
|
@@ -8,8 +8,8 @@ import type { CallableAbi } from '../../representation/model.js';
|
|
|
8
8
|
import type { RepresentationDeriver } from '../../representation/derive.js';
|
|
9
9
|
import type { CppArtifact } from './document.js';
|
|
10
10
|
import type { HostSpellings } from './host/host-members.js';
|
|
11
|
-
import { type CaptureIndex, type TemplateObjectDefinition } from './emit-context.js';
|
|
12
|
-
import type
|
|
11
|
+
import { type CaptureIndex, type CallableFactsSpelling, type TemplateObjectDefinition } from './emit-context.js';
|
|
12
|
+
import { type ConversionCensus } from '../../conversion/nodes.js';
|
|
13
13
|
import { type PrinterDrift } from './emit-narrowing.js';
|
|
14
14
|
import type { IntegerStorageFacts } from '../../ir/integers.js';
|
|
15
15
|
import { type InstantiationFacts } from '../../ir/instantiation.js';
|
|
@@ -41,5 +41,5 @@ import type { HostMethodAlias } from './host/host-method-aliases.js';
|
|
|
41
41
|
* the same namespace or an absent constant -- see the call site for why an
|
|
42
42
|
* absent arm is the one thing that may accompany it.
|
|
43
43
|
*/
|
|
44
|
-
export declare const emitBody: (body: IrBody, placements: ReadonlyMap<DeclarationId, BindingPlacement>, classes: ReadonlyMap<DeclarationId, ClassLayout>, hosts: HostSpellings, deriver: RepresentationDeriver, wellKnownSymbols: ReadonlyMap<DeclarationId, string> | undefined, captures: CaptureIndex | undefined, symbolKeys: Map<string, string>, templateObjects: Map<string, TemplateObjectDefinition>, directCallableBindings?: ReadonlyMap<DeclarationId, FunctionId>, virtualDispatch?: ReadonlyMap<string, CallableAbi>, narrowedStorage?: IntegerStorageFacts, narrowedFormals?: ReadonlySet<number>, repeatedConstructors?: ReadonlyMap<DeclarationId, DeclarationId>, dyingArguments?: ReadonlySet<IrValueId>, instantiation?: InstantiationFacts, abiOfCallable?: (callable: FunctionId) => IrBody["abi"], hostMethodAliases?: ReadonlyMap<DeclarationId, HostMethodAlias>, callableMemberCandidates?: ReadonlyMap<string, FunctionId>, borrowableMemberBodies?: ReadonlySet<FunctionId>, stableBorrowEntries?: ReadonlyMap<string, StableBorrowEntry>, printerDrift?: PrinterDrift[], conversions?: ConversionCensus | null, nativeSelections?: ReadonlyMap<string, NativeSelectionHelper> | undefined, callableIdentityDemand?: CallableIdentityDemand, nativeIntegrityRestricted?: boolean, fixedFieldStateConstant?: boolean) => readonly CppArtifact[];
|
|
44
|
+
export declare const emitBody: (body: IrBody, placements: ReadonlyMap<DeclarationId, BindingPlacement>, classes: ReadonlyMap<DeclarationId, ClassLayout>, hosts: HostSpellings, deriver: RepresentationDeriver, wellKnownSymbols: ReadonlyMap<DeclarationId, string> | undefined, captures: CaptureIndex | undefined, symbolKeys: Map<string, string>, templateObjects: Map<string, TemplateObjectDefinition>, directCallableBindings?: ReadonlyMap<DeclarationId, FunctionId>, virtualDispatch?: ReadonlyMap<string, CallableAbi>, narrowedStorage?: IntegerStorageFacts, narrowedFormals?: ReadonlySet<number>, repeatedConstructors?: ReadonlyMap<DeclarationId, DeclarationId>, dyingArguments?: ReadonlySet<IrValueId>, instantiation?: InstantiationFacts, abiOfCallable?: (callable: FunctionId) => IrBody["abi"], functionFacts?: ReadonlyMap<FunctionId, CallableFactsSpelling>, hostMethodAliases?: ReadonlyMap<DeclarationId, HostMethodAlias>, callableMemberCandidates?: ReadonlyMap<string, FunctionId>, borrowableMemberBodies?: ReadonlySet<FunctionId>, stableBorrowEntries?: ReadonlyMap<string, StableBorrowEntry>, printerDrift?: PrinterDrift[], conversions?: ConversionCensus | null, nativeSelections?: ReadonlyMap<string, NativeSelectionHelper> | undefined, callableIdentityDemand?: CallableIdentityDemand, nativeIntegrityRestricted?: boolean, fixedFieldStateConstant?: boolean) => readonly CppArtifact[];
|
|
45
45
|
export { cppConstructedThunkName, cppConstructThunkName, cppFormalName, cppReceiverName, cppThunkName, isCppEmitBlockedError } from './emit-context.js';
|
package/dist/targets/cpp/emit.js
CHANGED
|
@@ -7,7 +7,7 @@ import { constructedBaseOf } from '../../projection/classes.js';
|
|
|
7
7
|
import { allOperationsOf } from '../../ir/model.js';
|
|
8
8
|
import { resultOfIrOperation } from '../../ir/queries.js';
|
|
9
9
|
import { representationKey } from '../../representation/model.js';
|
|
10
|
-
import { cppFormalName, cppReceiverName, createCppEmitBlockedError, createEmitContext, defineValue, defineValueAlias, directCalleesOf, emptyCaptureIndex, isIntegerStorageValue, isCppEmitBlockedError, operandText, storageTypeOf,
|
|
10
|
+
import { cppFormalName, cppReceiverName, createCppEmitBlockedError, createEmitContext, defineValue, defineValueAlias, directCalleesOf, emptyCaptureIndex, isIntegerStorageValue, isCppEmitBlockedError, operandText, storageTypeOf, cppThunkEntryText, sealFactFieldsForRender } from './emit-context.js';
|
|
11
11
|
import { hostMemberReadsOf } from './host/emit-host-properties.js';
|
|
12
12
|
import { hostNamespaceReadsOf } from './host-namespace-reads.js';
|
|
13
13
|
import { functionSourceReadsOf } from './function-source-reads.js';
|
|
@@ -18,6 +18,7 @@ import { reactiveOriginsOf } from './reactive-origins.js';
|
|
|
18
18
|
import { renderTryRegion } from './emit-exceptions.js';
|
|
19
19
|
import { emitReturn } from './emit-return.js';
|
|
20
20
|
import { collectDirectBindingSinks, classObjectReadsOf, hostClassReadsOf, collectFormalCells, earlyCapturedCellPrologue, emitBindingRead, emitBindingWrite } from './emit-bindings.js';
|
|
21
|
+
import { EXACT_ARM_MATERIALIZER } from '../../conversion/nodes.js';
|
|
21
22
|
import { alignedValueText, emitMergeLiveArmRebuild, namedConversionText, widenedStoreText } from './emit-narrowing.js';
|
|
22
23
|
import { admitDenseWindows, collectCapacityHints, emitAllocateArrayObject, emitDenseSetup, emitFillLoop } from './emit-arrays.js';
|
|
23
24
|
import { noInstantiationFacts } from '../../ir/instantiation.js';
|
|
@@ -888,7 +889,12 @@ const emitConvert = (ctx, lines, operation) => {
|
|
|
888
889
|
// (`alignedValueText` asks the census for the same pair and renders the
|
|
889
890
|
// same node; a pair the census refused is its drift row, and the chain's).
|
|
890
891
|
const named = ctx.conversions.nodeById(operation.conversionUse);
|
|
891
|
-
|
|
892
|
+
// A coercion and an exact-arm projection each share their (source, target)
|
|
893
|
+
// pair with the store node `alignedValueText` would look up, so for those
|
|
894
|
+
// the instruction's own node is the only thing that says which one runs.
|
|
895
|
+
const namedOverPair = named?.capability.kind === 'coercion' ||
|
|
896
|
+
(named?.capability.kind === 'static' && named.capability.materializer.id === EXACT_ARM_MATERIALIZER);
|
|
897
|
+
const text = (namedOverPair
|
|
892
898
|
? namedConversionText(ctx, 'emit.ts:1026', named, sourceText)
|
|
893
899
|
: alignedValueText(ctx, 'emit.ts:1026', operation.source.representation, operation.result.representation, sourceText)) ??
|
|
894
900
|
// A method value escaping into a receiver-less slot: the receiver is
|
|
@@ -1456,7 +1462,7 @@ const settleDeadCalleeSideEffects = (ctx, operation) => {
|
|
|
1456
1462
|
// updating.
|
|
1457
1463
|
const carrier = operation.result.representation;
|
|
1458
1464
|
if (carrier.kind === 'function-value-dispatch' && carrier.abi.parameters.length === 0 && carrier.abi.receiver === null) {
|
|
1459
|
-
ctx.deferredTexts.set(operation.result.id, `${cppTypeOf(carrier)}{
|
|
1465
|
+
ctx.deferredTexts.set(operation.result.id, `${cppTypeOf(carrier)}{${cppThunkEntryText(ctx, operation.functionId)}, nullptr}`);
|
|
1460
1466
|
}
|
|
1461
1467
|
}
|
|
1462
1468
|
return;
|
|
@@ -1618,7 +1624,7 @@ export const emitBody = (body, placements, classes, hosts, deriver, wellKnownSym
|
|
|
1618
1624
|
symbolKeys, templateObjects, directCallableBindings = new Map(),
|
|
1619
1625
|
// `virtualDispatch` is every `class key` with a dispatch member (`virtual-methods.ts`);
|
|
1620
1626
|
// `narrowedStorage`/`narrowedFormals` are what the integer census settled here.
|
|
1621
|
-
virtualDispatch = new Map(), narrowedStorage = { reads: new Map(), integral: new Set(), magnitudes: new Map() }, narrowedFormals = new Set(), repeatedConstructors = new Map(), dyingArguments = new Set(), instantiation = noInstantiationFacts, abiOfCallable = () => null, hostMethodAliases = new Map(), callableMemberCandidates = new Map(), borrowableMemberBodies = new Set(), stableBorrowEntries = new Map(), printerDrift = [], conversions = null, nativeSelections = undefined, callableIdentityDemand = observesEveryCallableIdentity, nativeIntegrityRestricted = true, fixedFieldStateConstant = false) => {
|
|
1627
|
+
virtualDispatch = new Map(), narrowedStorage = { reads: new Map(), integral: new Set(), magnitudes: new Map() }, narrowedFormals = new Set(), repeatedConstructors = new Map(), dyingArguments = new Set(), instantiation = noInstantiationFacts, abiOfCallable = () => null, functionFacts = new Map(), hostMethodAliases = new Map(), callableMemberCandidates = new Map(), borrowableMemberBodies = new Set(), stableBorrowEntries = new Map(), printerDrift = [], conversions = null, nativeSelections = undefined, callableIdentityDemand = observesEveryCallableIdentity, nativeIntegrityRestricted = true, fixedFieldStateConstant = false) => {
|
|
1622
1628
|
// Every fact this body settles before a single line renders, computed here
|
|
1623
1629
|
// -- from `body` and the plain, already-available inputs above -- and
|
|
1624
1630
|
// handed to `createEmitContext` as one argument instead of mutated onto the
|
|
@@ -1707,7 +1713,7 @@ virtualDispatch = new Map(), narrowedStorage = { reads: new Map(), integral: new
|
|
|
1707
1713
|
directCallees: directCalleeFacts.directCallees,
|
|
1708
1714
|
directCalleeAbis: directCalleeFacts.directCalleeAbis
|
|
1709
1715
|
};
|
|
1710
|
-
const { ctx, prepass } = createEmitContext(body.abi, body.sourceOwner, placements, classes, hosts, deriver, bodyFacts, wellKnownSymbols, captures, symbolKeys, templateObjects, directCallableBindings, virtualDispatch, narrowedFormals, repeatedConstructors, dyingArguments, instantiation, abiOfCallable, hostMethodAliases, callableMemberCandidates, borrowableMemberBodies, stableBorrowEntries, printerDrift, conversions, nativeSelections, callableIdentityDemand, nativeIntegrityRestricted, fixedFieldStateConstant);
|
|
1716
|
+
const { ctx, prepass } = createEmitContext(body.abi, body.sourceOwner, placements, classes, hosts, deriver, bodyFacts, wellKnownSymbols, captures, symbolKeys, templateObjects, directCallableBindings, virtualDispatch, narrowedFormals, repeatedConstructors, dyingArguments, instantiation, abiOfCallable, functionFacts, hostMethodAliases, callableMemberCandidates, borrowableMemberBodies, stableBorrowEntries, printerDrift, conversions, nativeSelections, callableIdentityDemand, nativeIntegrityRestricted, fixedFieldStateConstant);
|
|
1711
1717
|
// `ownedValues` stays a genuine render-time OUTPUT buffer (`EmitContext`'s
|
|
1712
1718
|
// own doc: `defineValue` grows it as each operation's result is named) --
|
|
1713
1719
|
// unlike `ownedDyingValues` above, a formal argument's membership in it is
|
|
@@ -27,7 +27,7 @@ import { classesConstructedUnobservably, functionsIgnoringTheirReceiver } from '
|
|
|
27
27
|
import { renderJsonStructDeclarations } from './emit-json.js';
|
|
28
28
|
import { alignedValueText } from './emit-narrowing.js';
|
|
29
29
|
import { recordLayoutPolicyOf } from '../../projection/fields.js';
|
|
30
|
-
import { cppAbiParameterType, cppAbiType, cppStringLiteral,
|
|
30
|
+
import { cppAbiParameterType, cppAbiType, cppStringLiteral, cppBodyName, cppCallableDeclarationTagName, cppCommonJsModuleName, cppCommonJsRecordName, cppNarrowedIntegerType, cppRefcountedReceiver, cppBoxedType, cppClassName, cppConstructName, cppGlobalName, cppInitializeName, cppRecordFieldName, cppRecordStructName, cppResultTypeOf, cppTypeOf, sanitizeForCppIdentifier } from './types.js';
|
|
31
31
|
import { buildHostMethodAliasIndex } from './host/host-method-aliases.js';
|
|
32
32
|
import { cppRecursiveContainerDeclarations, cppRecursiveContainerTraceEdges } from './recursive-containers.js';
|
|
33
33
|
/**
|
|
@@ -562,7 +562,7 @@ const signatureOf = (body, captures, narrowed = new Set(), narrowedResult = fals
|
|
|
562
562
|
const formals = bodyFormalsOf(body.sourceOwner, effectiveAbi, admission, narrowed, borrowsReceiver(body), borrowed);
|
|
563
563
|
return `${result} ${name}(${formals.join(', ')})`;
|
|
564
564
|
};
|
|
565
|
-
const thunkOf = (body, captures, narrowed, narrowedResult, linkage
|
|
565
|
+
const thunkOf = (body, captures, narrowed, narrowedResult, linkage) => {
|
|
566
566
|
if (!body.abi)
|
|
567
567
|
return null;
|
|
568
568
|
const abi = body.abi;
|
|
@@ -601,17 +601,11 @@ const thunkOf = (body, captures, narrowed, narrowedResult, linkage, preserveFunc
|
|
|
601
601
|
const construct = constructThunkOf(body, abi, hasEnvironment, linkage);
|
|
602
602
|
return {
|
|
603
603
|
prototype: [`${signature};`, ...(construct === null ? [] : [construct.prototype])].join('\n'),
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
`[[maybe_unused]] static const bool ${cppThunkName(body.sourceOwner)}_source = ` +
|
|
610
|
-
`gea::CallableObject<${cppAbiType(abi)}>::registerSource<&${cppThunkName(body.sourceOwner)}>(` +
|
|
611
|
-
`${cppStringViewLiteral(body.functionName ?? '')}, ${body.functionLength ?? 0}, ${cppStringViewLiteral(body.functionSource)});`
|
|
612
|
-
]),
|
|
613
|
-
...(construct === null ? [] : [construct.definition])
|
|
614
|
-
].join('\n')
|
|
604
|
+
// No `registerSource` beside the thunk any more: the function's
|
|
605
|
+
// `name`/`length`/source text register at the sites that mint a function
|
|
606
|
+
// object from it (`emit-context.ts`'s `cppThunkEntryText`), so a function
|
|
607
|
+
// nothing reaches is not pinned into the binary by its own registration.
|
|
608
|
+
definition: [`${signature} { ${statement} }`, ...(construct === null ? [] : [construct.definition])].join('\n')
|
|
615
609
|
};
|
|
616
610
|
};
|
|
617
611
|
/**
|
|
@@ -1639,9 +1633,24 @@ export const renderTranslationUnit = (input) => {
|
|
|
1639
1633
|
}
|
|
1640
1634
|
return false;
|
|
1641
1635
|
});
|
|
1636
|
+
// The facts every mint site of a function registers (`cppThunkEntryText`),
|
|
1637
|
+
// spelled once per body here rather than looked up through the thunk. Empty
|
|
1638
|
+
// when the census above found no reader, so `&thunk` alone is stored.
|
|
1639
|
+
const functionFacts = new Map();
|
|
1640
|
+
if (preserveFunctionFacts)
|
|
1641
|
+
for (const body of input.bodies) {
|
|
1642
|
+
if (isRegionId(body.sourceOwner) || body.abi === null || body.functionSource === undefined)
|
|
1643
|
+
continue;
|
|
1644
|
+
functionFacts.set(body.sourceOwner, {
|
|
1645
|
+
abiType: cppAbiType(body.abi),
|
|
1646
|
+
name: body.functionName ?? '',
|
|
1647
|
+
length: body.functionLength ?? 0,
|
|
1648
|
+
source: body.functionSource
|
|
1649
|
+
});
|
|
1650
|
+
}
|
|
1642
1651
|
const thunks = new Map();
|
|
1643
1652
|
for (const body of input.bodies) {
|
|
1644
|
-
const thunk = thunkOf(body, captures, formalsNarrowedIn(body.sourceOwner), resultNarrowedIn(body.sourceOwner), linkage
|
|
1653
|
+
const thunk = thunkOf(body, captures, formalsNarrowedIn(body.sourceOwner), resultNarrowedIn(body.sourceOwner), linkage);
|
|
1645
1654
|
if (thunk)
|
|
1646
1655
|
thunks.set(body, thunk);
|
|
1647
1656
|
}
|
|
@@ -1713,7 +1722,7 @@ export const renderTranslationUnit = (input) => {
|
|
|
1713
1722
|
continue;
|
|
1714
1723
|
}
|
|
1715
1724
|
try {
|
|
1716
|
-
const sections = emitBody(body, input.placements, input.classes, hosts, deriver, input.wellKnownSymbols, captures, symbolKeys, templateObjects, directCallables, virtuals.dispatched, narrowedStorage.factsOf(body.sourceOwner), formalsNarrowedIn(body.sourceOwner), repeatedConstructors, dyingArguments, instantiation, (callable) => abiByBody.get(String(callable)) ?? null, hostMethodAliases, callableMemberCandidates, borrowableMemberBodies, stableBorrowEntries, printerDrift, input.conversions, selectionHelpers, callableIdentityDemand, nativeIntegrityRestricted, fixedFieldStateConstant);
|
|
1725
|
+
const sections = emitBody(body, input.placements, input.classes, hosts, deriver, input.wellKnownSymbols, captures, symbolKeys, templateObjects, directCallables, virtuals.dispatched, narrowedStorage.factsOf(body.sourceOwner), formalsNarrowedIn(body.sourceOwner), repeatedConstructors, dyingArguments, instantiation, (callable) => abiByBody.get(String(callable)) ?? null, functionFacts, hostMethodAliases, callableMemberCandidates, borrowableMemberBodies, stableBorrowEntries, printerDrift, input.conversions, selectionHelpers, callableIdentityDemand, nativeIntegrityRestricted, fixedFieldStateConstant);
|
|
1717
1726
|
const stableEntry = stableBorrowEntries.get(cppBodyName(body.sourceOwner));
|
|
1718
1727
|
const commonJsScope = commonJsOwner === null || commonJsOwner.nativeRecord
|
|
1719
1728
|
? []
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geastack/compiler",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.18",
|
|
4
4
|
"description": "The geastack TypeScript-to-C++ compiler: one semantic spine, no source-shaped authority, no boxing of statically typed values.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|