@geastack/compiler 1.0.15 → 1.0.17
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/integers.js +24 -2
- 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/ir/transfer.js +17 -8
- package/dist/plugins/apple/host.d.ts +2 -0
- package/dist/plugins/apple/host.js +13 -6
- 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/frontend.js +4 -1
- package/dist/semantics/model/operands.d.ts +17 -0
- package/dist/semantics/model/operations.d.ts +28 -1
- package/dist/semantics/normalize/flow/callable-reach.js +1 -3
- package/dist/semantics/normalize/global-host-mutations.js +2 -2
- 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/semantics/normalize/reachability.d.ts +20 -5
- package/dist/semantics/normalize/reachability.js +119 -14
- package/dist/semantics/normalize/specialization.js +1 -4
- package/dist/targets/cpp/class-layout.d.ts +40 -0
- package/dist/targets/cpp/class-layout.js +112 -0
- 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 +7 -6
- 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 +54 -1
- package/dist/targets/cpp/emit-properties.js +13 -3
- 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/prototype/emit-prototype-regexp.d.ts +1 -1
- package/dist/targets/cpp/prototype/emit-prototype-regexp.js +58 -2
- package/dist/targets/cpp/translation-unit.js +32 -16
- package/package.json +5 -5
- package/src/targets/cpp/runtime/gea_dynamic_proxy.h +38 -0
- package/src/targets/cpp/runtime/gea_runtime.h +247 -45
|
@@ -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';
|
|
@@ -523,7 +523,8 @@ const emitLazyArrowFieldCall = (ctx, lines, operation) => {
|
|
|
523
523
|
// conversion below then adapts the stored result to the read's, or refuses
|
|
524
524
|
// and declines the fusion.
|
|
525
525
|
const storedField = ctx.classes.get(site.owner)?.fields.find((field) => field.key === fieldName)?.representation ?? null;
|
|
526
|
-
const storedAbi = storedField !== null &&
|
|
526
|
+
const storedAbi = storedField !== null &&
|
|
527
|
+
(storedField.kind === 'function' || storedField.kind === 'function-family' || storedField.kind === 'function-value-dispatch')
|
|
527
528
|
? storedField.abi
|
|
528
529
|
: callee.abi;
|
|
529
530
|
const bodyAbi = ctx.abiOfCallable(plan.body);
|
|
@@ -1500,8 +1501,8 @@ export const emitAllocateCallable = (ctx, lines, operation) => {
|
|
|
1500
1501
|
// Brace-initializing this carrier with two values is what used to leave every
|
|
1501
1502
|
// pre-`class` constructor function in three.js's renderer unclaimed.
|
|
1502
1503
|
const pointers = payloadCarrier.kind === 'function-and-constructor'
|
|
1503
|
-
?
|
|
1504
|
-
:
|
|
1504
|
+
? `${cppThunkEntryText(ctx, operation.functionId)}, &${cppConstructedThunkName(operation.functionId)}`
|
|
1505
|
+
: cppThunkEntryText(ctx, operation.functionId);
|
|
1505
1506
|
// Tagged by the SOURCE DECLARATION, not by this copy's thunk. Two
|
|
1506
1507
|
// instantiations of one generic function are two thunks and one JavaScript
|
|
1507
1508
|
// function object; the runtime's own `CallableDeclarationTag` comment says
|
|
@@ -1550,11 +1551,11 @@ export const emitAllocateCallable = (ctx, lines, operation) => {
|
|
|
1550
1551
|
return `gea::Value::boxCallable<${abi.restFrom}>(${callable})`;
|
|
1551
1552
|
};
|
|
1552
1553
|
if (admission.kind === 'none') {
|
|
1553
|
-
lines.push(`${name} = gea::host::installOrdinaryConstructorPrototype(${boxText(`${callableType}{
|
|
1554
|
+
lines.push(`${name} = gea::host::installOrdinaryConstructorPrototype(${boxText(`${callableType}{${cppThunkEntryText(ctx, operation.functionId)}, nullptr}`)});`);
|
|
1554
1555
|
return;
|
|
1555
1556
|
}
|
|
1556
1557
|
const dynamicEnvironment = packedEnvironmentText(ctx, lines, operation.functionId, admission);
|
|
1557
|
-
lines.push(`${name} = gea::host::installOrdinaryConstructorPrototype(${boxText(`${callableType}{
|
|
1558
|
+
lines.push(`${name} = gea::host::installOrdinaryConstructorPrototype(${boxText(`${callableType}{${cppThunkEntryText(ctx, operation.functionId)}, ${dynamicEnvironment}}`)});`);
|
|
1558
1559
|
return;
|
|
1559
1560
|
}
|
|
1560
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
|
/**
|
|
@@ -3828,6 +3874,13 @@ const ownsItsStorage = (carrier) => {
|
|
|
3828
3874
|
return carrier.ownership === 'shared-refcount';
|
|
3829
3875
|
if (carrier.kind === 'optional')
|
|
3830
3876
|
return ownsItsStorage(carrier.payload);
|
|
3877
|
+
// A sum owns whatever its live arm owns. `gea::TaggedUnion` has a real move
|
|
3878
|
+
// constructor (`TaggedUnionOps::moveConstruct`), so a dying union hands its
|
|
3879
|
+
// string or reference over instead of `copyConstruct`ing it and then
|
|
3880
|
+
// destroying the original -- per header value, per request, in node-compat's
|
|
3881
|
+
// `writeHead`, where this was three heap copies of one content-type string.
|
|
3882
|
+
if (carrier.kind === 'tagged-union')
|
|
3883
|
+
return carrier.arms.some((arm) => ownsItsStorage(arm.value));
|
|
3831
3884
|
return false;
|
|
3832
3885
|
};
|
|
3833
3886
|
/**
|
|
@@ -592,7 +592,7 @@ export const emitGet = (ctx, lines, operation) => {
|
|
|
592
592
|
// struct-member fallthrough at the end would take `re.test` and emit
|
|
593
593
|
// `re->test`, which is a clang error naming a member instead of a refusal
|
|
594
594
|
// naming the method.
|
|
595
|
-
const regexpMember = regexpMemberText(ctx, operation.receiver, operation.key, operation.result.id, operation.result.representation, (fieldName, storage) => narrowedFieldReadText(ctx, operation, fieldName, storage)) ?? stringObjectMemberText(ctx, operation.receiver, operation.key, operation.result.representation);
|
|
595
|
+
const regexpMember = regexpMemberText(ctx, operation.receiver, operation.key, operation.result.id, operation.result.representation, (fieldName, storage, declared) => narrowedFieldReadText(ctx, operation, fieldName, storage, null, declared)) ?? stringObjectMemberText(ctx, operation.receiver, operation.key, operation.result.representation);
|
|
596
596
|
if (regexpMember !== null) {
|
|
597
597
|
// An empty rendering is a deferred RegExp.prototype method read -- see
|
|
598
598
|
// the identical comment on the array-access branch above.
|
|
@@ -911,8 +911,18 @@ const fixedFieldPresenceText = (ctx, receiver, fieldName) => {
|
|
|
911
911
|
* the original defect got in. A disagreement it declines to convert is a
|
|
912
912
|
* defect upstream, not a spelling this may invent, so that refuses by name.
|
|
913
913
|
*/
|
|
914
|
-
const narrowedFieldReadText = (ctx, operation, fieldName, storage, presence = null
|
|
915
|
-
|
|
914
|
+
const narrowedFieldReadText = (ctx, operation, fieldName, storage, presence = null,
|
|
915
|
+
/**
|
|
916
|
+
* The member's physical carrier, supplied by a caller that HAS it where the
|
|
917
|
+
* lookup below cannot. A compiler-owned native layout
|
|
918
|
+
* (`ExecResult`/`MatchResult`) never gets a sealed record layout, so
|
|
919
|
+
* `declaredFieldRepresentation` answers null for its members and this
|
|
920
|
+
* function would hand back the bare load -- correct until the read is
|
|
921
|
+
* narrowed, and a type error the moment it is. The caller that knows the
|
|
922
|
+
* struct says so instead of this function guessing.
|
|
923
|
+
*/
|
|
924
|
+
declaredStorage = null) => {
|
|
925
|
+
const declared = declaredStorage ?? declaredFieldRepresentation(ctx, operation.receiver, fieldName);
|
|
916
926
|
if (declared === null)
|
|
917
927
|
return storage;
|
|
918
928
|
const published = operation.result.representation;
|
|
@@ -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
|
|
@@ -72,7 +72,7 @@ export declare const regExpConstructionFromObject: (ctx: EmitContext, argument:
|
|
|
72
72
|
* Stated once; `regexpMemberText` and the prototype-read walk both ask it.
|
|
73
73
|
*/
|
|
74
74
|
export declare const deferredRegexpMethodClaim: (staticKeyTexts: ReadonlyMap<IrValueId, string>, receiver: IrOperand, key: IrOperand) => PrototypeMethodRead | null;
|
|
75
|
-
export declare const regexpMemberText: (ctx: EmitContext, receiver: IrOperand, key: IrOperand, result: IrValueId | null, resultRepresentation: Representation | undefined, narrow: (fieldName: string, storage: string) => string) => string | null;
|
|
75
|
+
export declare const regexpMemberText: (ctx: EmitContext, receiver: IrOperand, key: IrOperand, result: IrValueId | null, resultRepresentation: Representation | undefined, narrow: (fieldName: string, storage: string, declared: Representation | null) => string) => string | null;
|
|
76
76
|
/**
|
|
77
77
|
* `regexpStoreRefusal`'s whole decision, spelled as three outcomes rather than
|
|
78
78
|
* collapsed into "throws or doesn't":
|
|
@@ -216,6 +216,57 @@ const patternMethods = new Set(['test', 'exec', 'toString']);
|
|
|
216
216
|
* report group positions for non-participating groups at all.
|
|
217
217
|
*/
|
|
218
218
|
const resultDataMembers = new Set(['index', 'input', 'length', 'groups']);
|
|
219
|
+
/**
|
|
220
|
+
* What each of those members PHYSICALLY holds, per role.
|
|
221
|
+
*
|
|
222
|
+
* Stated here because nothing else can state it. These two structs are
|
|
223
|
+
* compiler-owned native layouts: `representation/derive.ts` deliberately does
|
|
224
|
+
* NOT seal a record layout for `RegExpExecArray`/`RegExpMatchArray` -- see its
|
|
225
|
+
* own comment on why sealing "a plausible-looking struct" for them would emit
|
|
226
|
+
* a program that compiles and is not a regular expression -- so
|
|
227
|
+
* `recordFieldsOfShape` answers nothing for their shape id and the generic
|
|
228
|
+
* `narrowedFieldReadText` has no declared carrier to reconcile against. It
|
|
229
|
+
* therefore returned the bare member load for every read, which is right only
|
|
230
|
+
* while the read is NOT narrowed.
|
|
231
|
+
*
|
|
232
|
+
* It is wrong the moment it is. `if (m.groups !== undefined) m.groups['id']`
|
|
233
|
+
* publishes the payload while the field stores the optional, and the bare load
|
|
234
|
+
* emitted `Optional<Ref<Dictionary<std::string>>>` where a
|
|
235
|
+
* `Ref<Dictionary<std::string>>` was wanted -- `->has` then resolved on
|
|
236
|
+
* `gea::Ref`, which has no such member, and every program that read a named
|
|
237
|
+
* capture group by key failed to compile (node-compat's `apps/http-parity`
|
|
238
|
+
* router was the first). The narrowing obligation the `narrow` parameter
|
|
239
|
+
* documents was being honoured at the call and dropped inside it, for want of
|
|
240
|
+
* this table.
|
|
241
|
+
*
|
|
242
|
+
* The three optionals are the two interfaces' own `?`, and they differ by
|
|
243
|
+
* role on purpose: `RegExpExecArray` declares `index`/`input` REQUIRED and
|
|
244
|
+
* `RegExpMatchArray` declares them optional, because a global pattern's match
|
|
245
|
+
* really does answer an array with neither. `length` is `double` on an exec
|
|
246
|
+
* result and the inherited `ArrayObject::length()` on a match result; neither
|
|
247
|
+
* is optional. Keep this in step with `ExecResult`/`MatchResult` in
|
|
248
|
+
* `runtime/gea_runtime.h` -- they are the same two facts, and clang checks
|
|
249
|
+
* only one of them.
|
|
250
|
+
*/
|
|
251
|
+
const namedGroupsStorage = {
|
|
252
|
+
kind: 'optional',
|
|
253
|
+
payload: { kind: 'dictionary', key: 'string', value: { kind: 'string' }, ownership: 'shared-refcount' },
|
|
254
|
+
absence: 'undefined'
|
|
255
|
+
};
|
|
256
|
+
const numberStorage = { kind: 'scalar', domain: 'number' };
|
|
257
|
+
const stringStorage = { kind: 'string' };
|
|
258
|
+
const optionalOf = (payload) => ({ kind: 'optional', payload, absence: 'undefined' });
|
|
259
|
+
const resultDataMemberStorage = (role, key) => {
|
|
260
|
+
if (key === 'groups')
|
|
261
|
+
return namedGroupsStorage;
|
|
262
|
+
if (key === 'length')
|
|
263
|
+
return numberStorage;
|
|
264
|
+
if (key === 'index')
|
|
265
|
+
return role === 'match-result' ? optionalOf(numberStorage) : numberStorage;
|
|
266
|
+
if (key === 'input')
|
|
267
|
+
return role === 'match-result' ? optionalOf(stringStorage) : stringStorage;
|
|
268
|
+
return null;
|
|
269
|
+
};
|
|
219
270
|
/** Whether `text` is a canonical array index -- a run of digits with no sign, point, or leading zero. */
|
|
220
271
|
const canonicalCaptureSlot = (text) => {
|
|
221
272
|
if (text.length === 0)
|
|
@@ -318,6 +369,11 @@ export const regexpMemberText = (ctx, receiver, key, result, resultRepresentatio
|
|
|
318
369
|
// fails to assign. The capture reads below do NOT go through it: they choose
|
|
319
370
|
// between `capture` and `capturedOrAbsent` on the published carrier already,
|
|
320
371
|
// and are not fields at all.
|
|
372
|
+
//
|
|
373
|
+
// `declared` is what the member physically holds. The caller cannot look it
|
|
374
|
+
// up for these two receivers -- their layout is never sealed -- so this
|
|
375
|
+
// supplies it from `resultDataMemberStorage`, and passing `null` (a RegExp
|
|
376
|
+
// pattern member) leaves the caller on its own lookup exactly as before.
|
|
321
377
|
narrow) => {
|
|
322
378
|
const role = regexpRoleOf(receiver.representation);
|
|
323
379
|
if (role === null)
|
|
@@ -349,7 +405,7 @@ narrow) => {
|
|
|
349
405
|
return unboxedReadText(resultRepresentation ?? { kind: 'dynamic', reason: 'declared-any-never-narrowed' }, `gea::runtime::regex::dynamicGet(${receiverText}, ${propertyKeyText(ctx, key, site)})`, site);
|
|
350
406
|
}
|
|
351
407
|
if (patternDataMembers.has(staticKey))
|
|
352
|
-
return narrow(staticKey, `${receiverText}->${staticKey}
|
|
408
|
+
return narrow(staticKey, `${receiverText}->${staticKey}`, null);
|
|
353
409
|
if (deferredRegexpMethodClaim(ctx.staticKeyTexts, receiver, key) !== null) {
|
|
354
410
|
if (result === null) {
|
|
355
411
|
throw createCppEmitBlockedError(`host-invocation:RegExp.prototype.${staticKey}`, `"${staticKey}" is a RegExp.prototype method, and this access publishes no value for its call to consume`);
|
|
@@ -369,7 +425,7 @@ narrow) => {
|
|
|
369
425
|
// `MatchResult`; one spelling for both compiled the exec result's read as
|
|
370
426
|
// a call on a `double`.
|
|
371
427
|
const member = staticKey === 'length' && role === 'match-result' ? 'length()' : staticKey;
|
|
372
|
-
return narrow(staticKey, `${receiverText}->${member}
|
|
428
|
+
return narrow(staticKey, `${receiverText}->${member}`, resultDataMemberStorage(role, staticKey));
|
|
373
429
|
}
|
|
374
430
|
if (canonicalCaptureSlot(staticKey))
|
|
375
431
|
return captureReadText(role, receiverText, staticKey, resultRepresentation);
|