diff --git a/packages/compiler/src/frontend/lowering/lower-functions.ts b/packages/compiler/src/frontend/lowering/lower-functions.ts index ea7d7957b..c1cca9c64 100644 --- a/packages/compiler/src/frontend/lowering/lower-functions.ts +++ b/packages/compiler/src/frontend/lowering/lower-functions.ts @@ -893,6 +893,9 @@ export function blockedBodyDiagnostics( lowerer.fnStack.push(ctx); let internal = false; try { + // A declared `this` parameter gives the body its receiver, as + // lowerFunction declares it. + if (decl.parameters.nodes.some(isThisParameter)) declareCallReceiver(lowerer, decl, false); for (const param of decl.parameters.nodes) { if (isThisParameter(param)) continue; const declaredParam = lowersCleanly(lowerer, () => { diff --git a/packages/runtime/src/scr_json.c b/packages/runtime/src/scr_json.c index 906be0ccd..fbfcade66 100644 --- a/packages/runtime/src/scr_json.c +++ b/packages/runtime/src/scr_json.c @@ -3968,10 +3968,14 @@ bool scr_bytes_instanceof_at(const ScrDyn *value, const ScrDyn *callee, const Sc bool undefined_prototype = prototype->kind == SCR_DYN_UNDEF; scr_dyn_release(prototype); /* A function the program compiled with no prototype (an arrow function - * or a method): Node's TypeError for an object, false for a primitive. - * The runtime's own constructors carry "native:" signatures. */ + * or a method): false for a primitive, and Node's TypeError for every + * object, typed arrays, native handles, promises and typed references + * included. The runtime's own constructors carry "native:" signatures. */ if (undefined_prototype && (!callee->v.fn.sig || strncmp(callee->v.fn.sig, "native:", 7) != 0)) { - if (value->kind != SCR_DYN_OBJ && value->kind != SCR_DYN_ARR && value->kind != SCR_DYN_FUNC) return false; + if (value->kind == SCR_DYN_UNDEF || value->kind == SCR_DYN_NULL || value->kind == SCR_DYN_BOOL || + value->kind == SCR_DYN_NUM || value->kind == SCR_DYN_STR || value->kind == SCR_DYN_BIGINT || + value->kind == SCR_DYN_SYMBOL) + return false; static const char message[] = "Function has non-object prototype 'undefined' in instanceof check"; scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1); return false; diff --git a/tests/corpus/7005-dyn-constructor-instanceof/main.ts b/tests/corpus/7005-dyn-constructor-instanceof/main.ts index c32a820c2..21fcbbd26 100644 --- a/tests/corpus/7005-dyn-constructor-instanceof/main.ts +++ b/tests/corpus/7005-dyn-constructor-instanceof/main.ts @@ -3,9 +3,10 @@ // an ES5 constructor's prototype, at module load and in calls, and throws a // TypeError for an arrow function, which has no prototype, when the value // is an object. -import { arrowCheck, describe, rejects, tags } from "tag-types"; +import { arrowCheck, arrowObjects, describe, rejects, tags } from "tag-types"; console.log(tags()); console.log(describe("{}"), describe("[1]"), describe("null"), describe('"s"')); console.log(rejects('{"tag":"!x"}')); console.log(arrowCheck("{}"), arrowCheck("1")); +console.log(arrowObjects()); diff --git a/tests/corpus/7005-dyn-constructor-instanceof/node_modules/tag-types/index.d.ts b/tests/corpus/7005-dyn-constructor-instanceof/node_modules/tag-types/index.d.ts index adeea728e..5b451adfa 100644 --- a/tests/corpus/7005-dyn-constructor-instanceof/node_modules/tag-types/index.d.ts +++ b/tests/corpus/7005-dyn-constructor-instanceof/node_modules/tag-types/index.d.ts @@ -2,3 +2,4 @@ export declare function describe(json: string): string; export declare function tags(): string; export declare function rejects(json: string): string; export declare function arrowCheck(json: string): string; +export declare function arrowObjects(): string; diff --git a/tests/corpus/7005-dyn-constructor-instanceof/node_modules/tag-types/index.js b/tests/corpus/7005-dyn-constructor-instanceof/node_modules/tag-types/index.js index 20eee362f..81165bd58 100644 --- a/tests/corpus/7005-dyn-constructor-instanceof/node_modules/tag-types/index.js +++ b/tests/corpus/7005-dyn-constructor-instanceof/node_modules/tag-types/index.js @@ -59,3 +59,13 @@ module.exports.arrowCheck = function (json) { return error.name + ": " + error.message; } }; +// Every object reaches the TypeError, typed arrays, promises and Maps too. +module.exports.arrowObjects = function () { + return [new Uint8Array(2), Promise.resolve(1), new Map(), [1]].map(function (value) { + try { + return String(value instanceof make); + } catch (error) { + return error.name; + } + }).join(","); +}; diff --git a/tests/coverage-fixtures/blocked-signature-body.ts b/tests/coverage-fixtures/blocked-signature-body.ts index 7f8db4eb8..2c37484e0 100644 --- a/tests/coverage-fixtures/blocked-signature-body.ts +++ b/tests/coverage-fixtures/blocked-signature-body.ts @@ -13,3 +13,7 @@ function unused(target: WeakRef): number { return extra === undefined ? 0 : 1; } console.log(measure(new WeakRef({}), "abc")); +// A declared `this` parameter gives the body its receiver. +function described(this: { label: string }, target: WeakRef): string { + return this.label + String(target); +} diff --git a/tests/harness/coverage.test.ts b/tests/harness/coverage.test.ts index 40b55b078..3ed562aea 100644 --- a/tests/harness/coverage.test.ts +++ b/tests/harness/coverage.test.ts @@ -194,17 +194,21 @@ test("the body of a signature-blocked function reports its diagnostics", () => { "SC2020 'WeakRef' is part of the standard library types but has no scriptc lowering yet", "SC2020 'new WeakRef' is part of the standard library types but has no scriptc lowering yet", ]); - // Nothing calls unused: its signature and body report as unreached. + // Nothing calls unused or described: their signatures and bodies report + // as unreached. described's declared `this` is its receiver, so + // `this.label` reports nothing. expect(sites(coverage.unreached?.diagnostics ?? [])).toEqual([ "SC2004 uses of 'extra' inherit the blocker on its declaration", + "SC2004 uses of 'target' inherit the blocker on its declaration", + "SC2020 'WeakRef' is part of the standard library types but has no scriptc lowering yet", "SC2020 'WeakRef' is part of the standard library types but has no scriptc lowering yet", "SC2020 'new WeakRef' is part of the standard library types but has no scriptc lowering yet", ]); // The bodies' statements still count once each, as not static. const un = coverage.unreached?.stats; - expect(coverage.stats.functionsSkipped + (un?.functionsSkipped ?? 0)).toBe(2); - expect(coverage.stats.statementsTotal + (un?.statementsTotal ?? 0)).toBe(6); - expect(coverage.stats.statementsFailed + (un?.statementsFailed ?? 0)).toBe(6); + expect(coverage.stats.functionsSkipped + (un?.functionsSkipped ?? 0)).toBe(3); + expect(coverage.stats.statementsTotal + (un?.statementsTotal ?? 0)).toBe(7); + expect(coverage.stats.statementsFailed + (un?.statementsFailed ?? 0)).toBe(7); }); test("deferred fences in a module's top-level code report as running at startup", () => {