diff --git a/docs/interpreter-construction.md b/docs/interpreter-construction.md index 51eda18..736ee8a 100644 --- a/docs/interpreter-construction.md +++ b/docs/interpreter-construction.md @@ -3,6 +3,7 @@ INT-OBJECT-01 adopts the following finite construction slices against CPython 3.14.7. `construction.json` records independent source/reference expectations; an authored reference is not evidence that the independent engine implements it. +O3 is implemented; O4/O5 remain reference-only until their changes are accepted. Classes remain explicit in classes-source/1 and bytecode 5. The default core-source/1 and bytecode 4 do not gain class syntax or reflective builtins. diff --git a/docs/interpreter-object-protocols.md b/docs/interpreter-object-protocols.md index b81c330..0042eb0 100644 --- a/docs/interpreter-object-protocols.md +++ b/docs/interpreter-object-protocols.md @@ -10,7 +10,8 @@ classes-source/1; core-source/1 keeps primitive arithmetic and generator support | --- | --- | | O1 | getattr(object, name[, default]), hasattr(object, name), setattr(object, name, value), delattr(object, name) on admitted class attributes. Existing descriptor precedence, Python callbacks and metadata rules apply. Names must be strings; only AttributeError is suppressed by defaults/hasattr. No lexical mangling is applied to dynamic strings. | | O2 | Binary/reflected/in-place slots for +, -, *, /, //, %, **, shifts and bitwise operators; unary +, -, ~; divmod and two/three-argument pow. Strict right-subclass reflected overrides have priority, same-type reflected fallback is suppressed, NotImplemented advances dispatch, and in-place slots fall back to binary dispatch. Results can be arbitrary owned Python values. | -| Future | __new__, super, richer type/isinstance/issubclass, builtin subclasses, metaclasses, custom attribute hooks, ordering, dir/vars/globals/locals/id, and dynamic exec/eval/compile/import remain independently gated. Matrix multiplication, user-valued power modulus dispatch and ordinary list/tuple/text repetition require separate slices. Existing bytes repetition and list in-place repetition accept the adopted index hook. | +| O3 | Allocation and initialization are adopted in the [construction contract](interpreter-construction.md), including __new__ and conditional initialization of its returned value. | +| Future | super, richer type/isinstance/issubclass, builtin subclasses, metaclasses, custom attribute hooks, ordering, dir/vars/globals/locals/id, and dynamic exec/eval/compile/import remain independently gated. Matrix multiplication, user-valued power modulus dispatch and ordinary list/tuple/text repetition require separate slices. Existing bytes repetition and list in-place repetition accept the adopted index hook. | Implicit operations consult type/MRO slots and descriptor binding, ignoring instance shadow attributes. Signature and callback failures propagate; a method @@ -28,7 +29,7 @@ and [attribute builtins](https://docs.python.org/3.14/library/functions.html#get ## Acceptance -The corpus contains 44 adopted comparisons and two explicit construction/hook +The corpus contains 45 adopted comparisons and one explicit attribute-hook refusals. CI runs Node and Chromium through draining and instruction-boundary drivers. Dedicated tests cover all twelve binary/reflected/in-place operator families, unary hooks, subclass priority, descriptor binding, sequence index diff --git a/runtime/class-policy.mjs b/runtime/class-policy.mjs index dd080db..e68ecea 100644 --- a/runtime/class-policy.mjs +++ b/runtime/class-policy.mjs @@ -9,7 +9,7 @@ export const CLASS_SPECIAL_POLICY = Object.freeze(Object.fromEntries([ ...Object.values(ARITHMETIC_SLOTS).flatMap(stem => ["", "r", ...(stem === "divmod" ? [] : ["i"])].map(prefix => ["__" + prefix + stem + "__", Object.freeze({...ordinarySlot, implicit: true})])), ...["__neg__", "__pos__", "__invert__"].map(name => [name, Object.freeze({...ordinarySlot, implicit: true})]), - ["__init__", ordinarySlot], ...DESCRIPTOR_HOOKS.map(name => [name, ordinarySlot]), + ["__init__", ordinarySlot], ["__new__", ordinarySlot], ...DESCRIPTOR_HOOKS.map(name => [name, ordinarySlot]), ["__module__", Object.freeze({define: true})], ["__qualname__", Object.freeze({define: true})] ])); export const isSpecialName = name => typeof name === "string" && name.startsWith("__") && name.endsWith("__"); diff --git a/runtime/class-policy.test.mjs b/runtime/class-policy.test.mjs index 86cc9cf..ab5dbff 100644 --- a/runtime/class-policy.test.mjs +++ b/runtime/class-policy.test.mjs @@ -22,7 +22,7 @@ test('shared policy is immutable and keeps metadata distinct from ordinary slot test('source and direct class records reject unadopted protocols through the same policy', () => { const runtime = new ClassRuntime(), root = runtime.root(); - for (const name of ['__matmul__', '__format__', '__new__', '__getattribute__']) { + for (const name of ['__matmul__', '__format__', '__prepare__', '__getattribute__']) { assert.equal(allowsClassSpecial(name, 'define'), false); assert.throws(() => compileSource('print("never")\nclass A:\n '+name+'=None\n', profile), {code: 'UNSUPPORTED'}); assert.throws(() => runtime.create('A', 'A', 'app', [], new Map([[name, null]]), root), {code: 'UNSUPPORTED'}); diff --git a/runtime/class-values.mjs b/runtime/class-values.mjs index 4b73ab7..aaa2789 100644 --- a/runtime/class-values.mjs +++ b/runtime/class-values.mjs @@ -46,7 +46,8 @@ export class ClassRuntime { if (!allowsClassSpecial(key, "define")) this.fail("UnsupportedOperationError", "Custom special methods require a later class stage.", "UNSUPPORTED"); } - if (namespace.has("__init__")) this.initializer(namespace.get("__init__")); + if (namespace.get("__new__") instanceof FunctionValue) + namespace.set("__new__", this.make(MethodWrapperValue, "staticmethod", namespace.get("__new__"))); for (const [key, expected] of [["__module__", module], ["__qualname__", qualname]]) if (namespace.has(key) && this.values.text(namespace.get(key)) !== expected) this.fail("UnsupportedOperationError", "Class metadata overrides are outside C1.", "UNSUPPORTED"); @@ -72,14 +73,20 @@ export class ClassRuntime { if (!(type instanceof ClassValue)) throw new TypeError("An adopted class is required."); this.touch(type); return this.make(InstanceValue, type); } - lookup(type, name) { + hasSlot(type, name) { + // The bounded MRO is also the native slot-presence index. Default calls keep + // their original lookup/accounting path; only adopted overrides add phases. + return type.mro.some(base => !base.root && base.namespace.has(name)); + } + lookup(type, name, native = true) { this.key(name); this.touch(type); if (!(type instanceof ClassValue)) throw new TypeError("An adopted class is required."); for (const base of type.mro) { this.charge(1); + if (!native && base.root && ["__new__", "__init__"].includes(name)) continue; if (base.namespace.has(name)) return {found: true, value: base.namespace.get(name)}; - if (base.root && name === "__hash__") { - const value = this.make(BuiltinValue, "object.__hash__"); + if (native && base.root && ["__hash__", "__new__", "__init__"].includes(name)) { + const value = this.make(BuiltinValue, "object." + name); base.namespace.set(name, value); try { const layout = valueLayout(base); this.memory?.resize(base, layout.bytes, layout.children); } catch (error) { base.namespace.delete(name); throw error; } @@ -151,7 +158,7 @@ export class ClassRuntime { if (value instanceof MethodWrapperValue) return {kind: "value", value: value.kind === "staticmethod" ? value.callable : this.make(BoundMethodValue, value.callable, owner)}; return {kind: "value", value: receiver !== null && (value instanceof FunctionValue || - value instanceof BuiltinValue && value.name === "object.__hash__") ? this.make(BoundMethodValue, value, receiver) : value}; + value instanceof BuiltinValue && ["object.__hash__", "object.__init__"].includes(value.name)) ? this.make(BoundMethodValue, value, receiver) : value}; } special(receiver, name) { this.key(name); this.touch(receiver); @@ -190,7 +197,6 @@ export class ClassRuntime { this.fail("UnsupportedOperationError", "Attribute mutation requires a class or instance.", "UNSUPPORTED"); if (receiver instanceof ClassValue && receiver.root) this.fail("TypeError", "Cannot mutate the immutable object type."); if (!allowsClassSpecial(name, "mutate")) this.fail("UnsupportedOperationError", "Special attribute mutation is outside classes-source/1.", "UNSUPPORTED"); - if (receiver instanceof ClassValue && name === "__init__" && !remove) this.initializer(value); const map = receiver instanceof ClassValue ? receiver.namespace : receiver.attributes; const present = map.has(name), before = map.get(name); if (remove && !present) this.fail("AttributeError", "Class/instance has no attribute '" + name + "'."); @@ -223,11 +229,6 @@ export class ClassRuntime { }); return this.make(PropertyValue, ...fields.map(name => assigned.has(name) ? assigned.get(name) : null)); } - initializer(value) { - if (value instanceof MethodWrapperValue) value = value.callable; - if (!(value instanceof FunctionValue || value instanceof BoundMethodValue && value.callable instanceof FunctionValue)) - this.fail("UnsupportedOperationError", "Initializers require Python functions, retained Python bound methods or native method wrappers.", "UNSUPPORTED"); - } isinstance(value, type) { if (!(type instanceof ClassValue)) this.fail("TypeError", "isinstance requires an adopted class."); this.touch(type); this.touch(value); diff --git a/runtime/class-values.test.mjs b/runtime/class-values.test.mjs index 4285633..d9c3ecd 100644 --- a/runtime/class-values.test.mjs +++ b/runtime/class-values.test.mjs @@ -76,8 +76,6 @@ test("identity/truth, metadata and explicit unsupported boundaries remain separa assert.throws(() => s.runtime.mutate(A, "__bases__", null), {code: "UNSUPPORTED"}); assert.throws(() => s.runtime.mutate(s.root, "value", 7n), {name: "TypeError"}); assert.throws(() => s.make("Special", [], new Map([["__matmul__", method("f")]])), {code: "UNSUPPORTED"}); - assert.throws(() => s.make("BadInit", [], new Map([["__init__", A]])), {code: "UNSUPPORTED"}); - assert.throws(() => s.runtime.mutate(A, "__init__", s.runtime.make(BoundMethodValue, A, a)), {code: "UNSUPPORTED"}); assert.throws(() => s.runtime.subclass(a, A), {name: "TypeError"}); } finally { s.close(); } }); diff --git a/runtime/construction.test.mjs b/runtime/construction.test.mjs new file mode 100644 index 0000000..e91fe3e --- /dev/null +++ b/runtime/construction.test.mjs @@ -0,0 +1,79 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import {runSourceRequest} from './source-request.mjs'; +import {compileSourceModules} from './source-modules.mjs'; +import {ModuleSession} from './module-session.mjs'; +import {Execution} from './functions.mjs'; +import {completion, completionRoots} from './operations.mjs'; + +const profile = {languageProfile: 'classes-source/1'}; +const unit = source => ({name: 'app', package: false, imports: [], source, notice: 'Synthetic construction fixture'}); +const run = (source, options = {}, memoryLimits) => runSourceRequest({modules: [unit(source)], compileOptions: profile, options, memoryLimits}); +const closed = stats => { for (const key of ['bytes', 'objects', 'roots', 'frames', 'external']) assert.equal(stats[key], 0, key); }; + +test('allocation continuations retain the class, original operands and outer destinations', () => { + const type = {}, argument = {}, keyword = {}, outer = {}, after = completion('relay', {receiver: outer}); + const target = completion('new-result', {type, args: [argument], keywords: ['x'], keywordValues: [keyword], depth: 1, after}); + assert.deepEqual(completionRoots(target), [type, argument, keyword, outer]); +}); + +test('constructor callbacks and bound initializer descriptors survive collection, cancellation and close', async () => { + const sources = [ + 'class D:\n def __get__(self,obj,owner):\n print("entered")\n def create(cls,x,*,y): return object.__new__(cls)\n return create\nclass A:\n __new__=D()\n def __init__(self,x,*,y): self.x=x[0]+y[0]\nprint([1][0]+A([3],y=[4]).x)\n', + 'class D:\n def __get__(self,obj,owner):\n print("entered")\n def initialize(x,*,y): obj.x=x[0]+y[0]\n return initialize\nclass A:\n def __new__(cls,*args,**kwargs): return object.__new__(cls)\n __init__=D()\nprint([1][0]+A([3],y=[4]).x)\n', + ]; + for (const source of sources) { + const {registry} = await compileSourceModules([unit(source)], profile); + try { for (const mode of ['finish', 'cancel', 'close']) { + const session = new ModuleSession(registry); let output = '', cancel = false, reached = false; + session.context.begin({shouldCancel: () => cancel}, text => { output += text; }); const execution = Execution.modules(session.context); + try { + while (execution.state !== 'completed') { + execution.advance(); session.memory.collect([], {cleanup: true}); + if (output === 'entered\n') { + reached = true; + if (mode === 'cancel') { cancel = true; assert.throws(() => execution.advance(), {code: 'CANCELLED'}); break; } + if (mode === 'close') break; + } + } + assert.ok(reached); if (mode === 'finish') assert.equal(output, 'entered\n8\n'); + } finally { execution.close(); execution.close(); session.context.finish(); session.close(); } + closed(session.stats()); + } } finally { registry.close(); } + } +}); + +test('construction shares Python recursion, work and real allocation limits', async () => { + for (const slot of ['__new__', '__init__']) for (const [body, options, limits, expected] of [ + [slot === '__new__' ? 'return cls()' : 'self.__init__()', {maxDepth: 12}, undefined, 'PYTHON'], + ['while True: pass', {maxSteps: 200}, undefined, 'BUDGET'], + ['a=[]\n while True: a.append([7])', {maxSteps: 100000}, {maxBytes: 22000}, 'OOM'], + ]) { + const result = await run('class A:\n def '+slot+'('+(slot === '__new__' ? 'cls' : 'self')+'):\n '+body+'\nA()\n', options, limits); + assert.equal(result.error.code, expected); if (expected === 'PYTHON') assert.equal(result.error.type, 'RecursionError'); closed(result.closed); + } +}); + +test('constructor binding and body errors retain their original source frames', async () => { + for (const slot of ['__new__', '__init__']) { + const result = await run('class A:\n def '+slot+'(value):\n raise AttributeError("original")\nA()\n'); + assert.equal(result.error.type, 'AttributeError'); + assert.deepEqual(result.error.traceback.map(frame => [frame.function, frame.location.span[0]]), [['', 4], [slot, 3]]); + closed(result.closed); + } +}); + +test('reading native constructors does not turn default class calls into custom slots', async () => { + const result = await run('new=object.__new__\ninit=object.__init__\nclass A: pass\ntry: A(1)\nexcept TypeError: print("arguments")\nprint(isinstance(A(),A))\n'); + assert.equal(result.error, undefined, JSON.stringify(result.error)); assert.equal(result.stdout, 'arguments\nTrue\n'); closed(result.closed); +}); + +test('initializer callability is checked at invocation after allocation effects', async () => { + const result = await run('class A:\n def __new__(cls):\n print("allocated")\n return object.__new__(cls)\n __init__=None\nA()\n'); + assert.equal(result.error.type, 'TypeError'); assert.equal(result.stdout, 'allocated\n'); closed(result.closed); +}); + +test('initialization after custom allocation ignores instance shadow attributes', async () => { + const result = await run('class A:\n def __new__(cls):\n value=object.__new__(cls)\n value.__init__=None\n return value\n def __init__(self): self.x=7\nprint(A().x)\n'); + assert.equal(result.error, undefined, JSON.stringify(result.error)); assert.equal(result.stdout, '7\n'); closed(result.closed); +}); diff --git a/runtime/operation-runtime.mjs b/runtime/operation-runtime.mjs index 020ed8a..21e4365 100644 --- a/runtime/operation-runtime.mjs +++ b/runtime/operation-runtime.mjs @@ -1,6 +1,6 @@ import {ARITHMETIC_SLOTS} from "./class-policy.mjs"; import {GeneratorValue, FunctionValue, CallArguments, BuiltinValue, AppendValue, ValueMethod, ExceptionValue, EXCEPTIONS, matchesException, ClassValue, InstanceValue, BoundMethodValue, PropertyValue, MethodWrapperValue, PropertyAccessorValue, ProtocolIteratorValue, NotImplementedValue} from "./function-objects.mjs"; -import {completion as continuation, callTask, unaryTask, valueTask, itemTask, sliceTask, containsTask, compareTask, dictionaryTask, dictionaryBuildTask, arithmeticTask, numericTask, consumeTask, operationRoots} from "./operations.mjs"; +import {completion as continuation, completionRoots, callTask, unaryTask, valueTask, itemTask, sliceTask, containsTask, compareTask, dictionaryTask, dictionaryBuildTask, arithmeticTask, numericTask, consumeTask, operationRoots} from "./operations.mjs"; export class Raised { constructor(value) { this.value = value; } } @@ -335,6 +335,11 @@ export function operationRuntime({state, memory, values, classes, moduleContext, } completion = completion.after; } + if (completion?.kind === "new-result") { + state.temporaries.push(value, ...completionRoots(completion)); + if (!(value instanceof InstanceValue) || !classes.subclass(value.type, completion.type)) return valueTask(value, site, completion.after); + return initialize(value, completion.args, completion.keywords, completion.keywordValues, site, completion.after, completion.depth, true); + } if (completion?.kind === "arithmetic-result") { if (!(value instanceof NotImplementedValue)) return valueTask(value, site, completion.after); const inplace = completion.operation.startsWith("inplace-"); @@ -576,6 +581,17 @@ export function operationRuntime({state, memory, values, classes, moduleContext, } return deliver(type, null, site); }; + const initialize = (instance, args, keywords, keywordValues, site, after, depth, customNew = false) => { + const initializer = classes.lookup(instance.type, "__init__", false); + if (!initializer.found) { + if (!customNew && (args.length || keywords.length)) python("TypeError", instance.type.name + "() takes no arguments"); + return valueTask(instance, site, after); + } + const plan = customNew ? classes.bindAttribute(initializer.value, instance, instance.type) : classes.read(instance, "__init__"); + const target = continuation("init", {instance, after}); + return plan.kind === "value" ? callTask(plan.value, args, keywords, keywordValues, site, target, depth) : + callTask(plan.callable, plan.args, [], [], site, continuation("invoke", {args, keywords, keywordValues, after: target, depth}), depth); + }; const printNext = (args, index, site, after, depth, prefix = "") => { state.temporaries.push(...args); let text = prefix; @@ -1082,15 +1098,17 @@ export function operationRuntime({state, memory, values, classes, moduleContext, task = deliver(allocate(PropertyValue, ...fields), completion, site); continue; } if (callable instanceof ClassValue) { - const instance = classes.instance(callable); state.temporaries.push(instance); - const initializer = classes.lookup(callable, "__init__"); - if (!initializer.found) { - if (args.length || keywords.length) python("TypeError", callable.name + "() takes no arguments"); - task = deliver(instance, completion, site); continue; + if (classes.hasSlot(callable, "__new__")) { + if (++depth > maxDepth) python("RecursionError", "maximum constructor dispatch depth exceeded"); + const item = classes.lookup(callable, "__new__"), plan = classes.bindAttribute(item.value, null, callable); + const target = continuation("new-result", {type: callable, args, keywords, keywordValues, after: completion, depth}); + const positional = [callable, ...args]; + task = plan.kind === "value" ? callTask(plan.value, positional, keywords, keywordValues, site, target, depth) : + callTask(plan.callable, plan.args, [], [], site, continuation("invoke", {args: positional, keywords, keywordValues, after: target, depth}), depth); + continue; } - const method = classes.get(instance, "__init__"); - task = callTask(method, args, keywords, keywordValues, site, - continuation("init", {instance, after: completion}), depth); continue; + const instance = classes.instance(callable); state.temporaries.push(instance); + task = initialize(instance, args, keywords, keywordValues, site, completion, depth); continue; } if (callable instanceof FunctionValue) { const bindings = bind(callable, args, keywords, keywordValues); @@ -1100,6 +1118,21 @@ export function operationRuntime({state, memory, values, classes, moduleContext, enter(callable, bindings, site, completion); return; } if (callable instanceof BuiltinValue) { + if (callable.name === "object.__new__") { + if (!args.length || !(args[0] instanceof ClassValue)) python("TypeError", "object.__new__ requires a type"); + const type = args[0]; + if ((args.length > 1 || keywords.length) && (classes.hasSlot(type, "__new__") || !classes.hasSlot(type, "__init__"))) + python("TypeError", "object.__new__ received extra arguments"); + task = valueTask(classes.instance(type), site, completion); continue; + } + if (callable.name === "object.__init__") { + if (!args.length) python("TypeError", "object.__init__ requires an instance"); + const value = args[0]; + if ((args.length > 1 || keywords.length) && (!(value instanceof InstanceValue) || + classes.hasSlot(value.type, "__init__") || !classes.hasSlot(value.type, "__new__"))) + python("TypeError", "object.__init__ received extra arguments"); + task = valueTask(null, site, completion); continue; + } if (["getattr", "setattr", "delattr", "hasattr"].includes(callable.name)) { const name = callable.name, writing = name === "setattr" || name === "delattr"; if (keywords.length || args.length < 2 || args.length > (name === "getattr" || name === "setattr" ? 3 : 2) || name === "setattr" && args.length !== 3) diff --git a/runtime/operations.mjs b/runtime/operations.mjs index 8ab5539..654faa7 100644 --- a/runtime/operations.mjs +++ b/runtime/operations.mjs @@ -36,6 +36,10 @@ export function completion(kind, fields = {}) { switch (kind) { case "class": record = {kind, bases: operands(fields.bases), module: fields.module}; break; case "init": record = {kind, instance: fields.instance, after}; break; + case "new-result": + record = {kind, type: fields.type, args: operands(fields.args), keywords: operands(fields.keywords), + keywordValues: operands(fields.keywordValues), depth: fields.depth, after}; + depthCheck(record.depth); break; case "relay": record = {kind, receiver: fields.receiver, after}; break; case "item-result": record = {kind, receiver: fields.receiver, after}; break; case "index-result": record = {kind, receiver: fields.receiver, after}; break; @@ -150,6 +154,7 @@ function visit(record) { switch (record.kind) { case "class": case "condition": case "discard": case "for-iter": case "for-next": case "for-exhausted": return []; case "init": return [record.instance, ...completionRoots(record.after)]; + case "new-result": return [record.type, ...record.args, ...record.keywordValues, ...completionRoots(record.after)]; case "relay": case "protocol-result": case "optional-truth": case "format-result": case "index-result": case "item-result": return [record.receiver, ...completionRoots(record.after)]; case "hash-result": return [record.receiver, ...completionRoots(record.after)]; case "numeric-result": case "numeric-index": return [...record.args, ...completionRoots(record.after)]; diff --git a/src/pocket_python/_interpreter_lock.py b/src/pocket_python/_interpreter_lock.py index 4f9d14e..8ad4ab5 100644 --- a/src/pocket_python/_interpreter_lock.py +++ b/src/pocket_python/_interpreter_lock.py @@ -1,11 +1,11 @@ """Known source-console assets, canonical LF bytes; refresh with scripts/lock_interpreter.py.""" -ENGINE_FINGERPRINT = '0482efe29e46d5f1228ee6d7ce214f1f4168729a55241e4995502e9587cb3ec2' +ENGINE_FINGERPRINT = '27fbf19fccda89eed8d913ad2a5a10ef540ef838a614047248fbdc88e94bfd75' ENGINE_FILES = ( ('browser-protocol.mjs', 10589, '920e8d07c7070995636dff40fdf4333afc9bd4143483db5d8dff82ec1f28ff76'), ('bytecode.mjs', 6836, 'e76fabeea879bc1da8016283f7e74df5974479666b100199c2d3c31a53b8ecf4'), - ('class-policy.mjs', 1968, 'e3275e080c2d26584bb318e88d9a0278191815ac072c48e3054fd4c908cb771b'), - ('class-values.mjs', 15372, '67fc606a6c91f7aeb1d4309cc4a0be33778181cdccb51190e5f76d4969d9b43d'), + ('class-policy.mjs', 1995, '1e705bafb0c8d32a1bc49bb066d4284d1998db919fc5cfbc156afe06104b610b'), + ('class-values.mjs', 15409, '7c69d186e0a36bf85f974b6ef9ddd8c1e0399fd1d155b14daa9e52cdcd70de3f'), ('function-bytecode.mjs', 20438, 'ca6b3abd73d2be35422eea244b876a851bf98e38836e31a33fa6d18d4252ae68'), ('function-objects.mjs', 4277, '06af0ca936de1dd336cb7a2ac13f24efae960ea3fe448c93fb95a2e92213c4e2'), ('functions.mjs', 57726, '7d6e4322c31187b12387695dd094a0fd4dab2220cf28c1df2bb4856ccf7ae179'), @@ -20,8 +20,8 @@ ('module-registry.mjs', 10085, 'f23f8313c0804a052cba155e193df920b0221ef978a06f16c1a22f208dfbef91'), ('module-session.mjs', 1470, '08e4ccf16361299c15b99621fdfcef0d9ef93de50d28d738158c7bc85a7c23a3'), ('numeric.mjs', 13954, '2431f099a7d40aa38d6b3c68b556207752e4191211e01005acde94325bd91bad'), - ('operation-runtime.mjs', 93154, '488791c8e00d8e510feaf9bc654f06a6e0bcaed7f8ed6a2024a1a23140713315'), - ('operations.mjs', 23452, 'bd4c064811e6400576cae60bfcdbd35a76c829a25085794f95520d6307fad78e'), + ('operation-runtime.mjs', 95840, '09baff12598d313ae411fff8f3154521341b527b6e294db7f2ebdc9608c17fcd'), + ('operations.mjs', 23825, '4cf2afcb1d8fee89e4049c2fd176f230db17f57e6b4a4a6048b8004fedfe0684'), ('source-compiler.mjs', 35239, '4201a25a0c45b8fba4777c8e98b75e37f3277888708093a77e2827dec5ff7063'), ('source-identifiers.mjs', 25949, '1e4e6a90728a2d9393eb4b2055d4bd759542f4768d1870b508d629c6888e8914'), ('source-modules.mjs', 5115, '3f824a8cf79a9ab78c1b817ed1597d114d851748a6b0894a4ac6b4bdbe01cfef'), diff --git a/tests/fixtures/interpreter/construction.json b/tests/fixtures/interpreter/construction.json index 33d77fb..feaa9ad 100644 --- a/tests/fixtures/interpreter/construction.json +++ b/tests/fixtures/interpreter/construction.json @@ -3,7 +3,9 @@ "suite": "interpreter-construction/1", "profile": "classes-source/1", "reference_version": "3.14.7", - "implemented_stages": [], + "implemented_stages": [ + "O3" + ], "cases": [ { "id": "new-original-arguments", diff --git a/tests/fixtures/interpreter/length-truth.json b/tests/fixtures/interpreter/length-truth.json index e331527..3ac3aab 100644 --- a/tests/fixtures/interpreter/length-truth.json +++ b/tests/fixtures/interpreter/length-truth.json @@ -604,7 +604,7 @@ }, { "id": "future-new", - "stage": "C3-future", + "stage": "C3-length", "source": "class Index:\n def __new__(cls): return object()\nvalue=Index()\n", "stdout": "" } diff --git a/tests/fixtures/interpreter/object-protocols.json b/tests/fixtures/interpreter/object-protocols.json index 96e258c..62d1047 100644 --- a/tests/fixtures/interpreter/object-protocols.json +++ b/tests/fixtures/interpreter/object-protocols.json @@ -5,7 +5,8 @@ "reference_version": "3.14.7", "implemented_stages": [ "O1", - "O2" + "O2", + "O3" ], "cases": [ { @@ -88,7 +89,7 @@ }, { "id": "new-future", - "stage": "Future", + "stage": "O3", "source": "class A:\n def __new__(cls): return object.__new__(cls)\nprint(type(A()) is A)\n", "stdout": "True\n" }, diff --git a/tests/fixtures/interpreter/special-calls.json b/tests/fixtures/interpreter/special-calls.json index fc8902e..3bf6dcd 100644 --- a/tests/fixtures/interpreter/special-calls.json +++ b/tests/fixtures/interpreter/special-calls.json @@ -407,7 +407,7 @@ }, { "id": "future-new", - "stage": "C3-future", + "stage": "C3-call", "source": "class A:\n def __new__(cls): return 7\nprint(A())\n", "stdout": "7\n" },