diff --git a/src/serializer/functions.js b/src/serializer/functions.js index 8bfb5ce975..6e97e56808 100644 --- a/src/serializer/functions.js +++ b/src/serializer/functions.js @@ -38,6 +38,7 @@ import { handleReportedSideEffect } from "./utils.js"; import { ShapeInformation } from "../utils/ShapeInformation"; import type { ArgModel } from "../utils/ShapeInformation"; import * as t from "@babel/types"; +import { stringOfLocation } from "../utils/babelhelpers"; type AdditionalFunctionEntry = { value: ECMAScriptSourceFunctionValue | AbstractValue, @@ -299,7 +300,11 @@ export class Functions { let conflicts: Map = new Map(); for (let fun1 of additionalFunctions) { invariant(fun1 instanceof FunctionValue); - let fun1Name = this.functionExpressions.get(fun1) || fun1.intrinsicName || "(unknown function)"; + let fun1Location = fun1.expressionLocation; + let fun1Name = + this.functionExpressions.get(fun1) || + fun1.getDebugName() || + `(unknown function ${fun1Location ? stringOfLocation(fun1Location) : ""})`; // Also do argument validation here let additionalFunctionEffects = this.writeEffects.get(fun1); invariant(additionalFunctionEffects !== undefined); @@ -318,8 +323,13 @@ export class Functions { for (let fun2 of additionalFunctions) { if (fun1 === fun2) continue; invariant(fun2 instanceof FunctionValue); + let fun2Location = fun2.expressionLocation; + let fun2Name = + this.functionExpressions.get(fun2) || + fun2.getDebugName() || + `(unknown function ${fun2Location ? stringOfLocation(fun2Location) : ""})`; let reportFn = () => { - this.reportWriteConflicts(fun1Name, conflicts, e1.modifiedProperties, this._callOfFunction(fun2)); + this.reportWriteConflicts(fun1Name, fun2Name, conflicts, e1.modifiedProperties, this._callOfFunction(fun2)); return null; }; let fun2Effects = this.writeEffects.get(fun2); @@ -344,14 +354,26 @@ export class Functions { } reportWriteConflicts( - fname: string, + f1name: string, + f2name: string, conflicts: Map, pbs: PropertyBindings, call2: void => Value ): void { - let reportConflict = (location: BabelNodeSourceLocation) => { + let reportConflict = ( + location: BabelNodeSourceLocation, + object: string = "", + key?: string, + originalLocation: BabelNodeSourceLocation | void | null + ) => { + let firstLocationString = originalLocation ? `${stringOfLocation(originalLocation)}` : ""; + let propString = key ? ` "${key}"` : ""; + let objectString = object ? ` on object "${object}" ` : ""; + if (!objectString && key) objectString = " on "; let error = new CompilerDiagnostic( - `Property access conflicts with write in optimized function ${fname}`, + `Write to property${propString}${objectString}at optimized function ${f1name}[${firstLocationString}] conflicts with access in function ${f2name}[${ + location.start.line + }:${location.start.column}]`, location, "PP1003", "FatalError" @@ -366,14 +388,22 @@ export class Functions { this.realm.reportObjectGetOwnProperties = (ob: ObjectValue) => { let location = this.realm.currentLocation; invariant(location); - if (writtenObjects.has(ob) && !conflicts.has(location)) reportConflict(location); + if (writtenObjects.has(ob) && !conflicts.has(location)) + reportConflict(location, ob.getDebugName(), undefined, ob.expressionLocation); }; let oldReportPropertyAccess = this.realm.reportPropertyAccess; this.realm.reportPropertyAccess = (pb: PropertyBinding) => { if (ObjectValue.refuseSerializationOnPropertyBinding(pb)) return; let location = this.realm.currentLocation; if (!location) return; // happens only when accessing an additional function property - if (pbs.has(pb) && !conflicts.has(location)) reportConflict(location); + if (pbs.has(pb) && !conflicts.has(location)) { + let originalLocation = + pb.descriptor && pb.descriptor.value && !Array.isArray(pb.descriptor.value) + ? pb.descriptor.value.expressionLocation + : undefined; + let keyString = pb.key instanceof Value ? pb.key.toDisplayString() : pb.key; + reportConflict(location, pb.object ? pb.object.getDebugName() : undefined, keyString, originalLocation); + } }; try { ignoreErrorsIn(this.realm, () => this.realm.evaluateForEffectsInGlobalEnv(call2)); diff --git a/src/values/FunctionValue.js b/src/values/FunctionValue.js index ff677f2095..2c603ecc26 100644 --- a/src/values/FunctionValue.js +++ b/src/values/FunctionValue.js @@ -53,4 +53,8 @@ export default class FunctionValue extends ObjectValue { hasDefaultLength(): boolean { invariant(false, "abstract method; please override"); } + + getDebugName(): string { + return super.getDebugName() || this.getName(); + } } diff --git a/src/values/Value.js b/src/values/Value.js index 0e32523a68..6c1fb10251 100644 --- a/src/values/Value.js +++ b/src/values/Value.js @@ -193,4 +193,8 @@ export default class Value { _serialize(set: Function, stack: Map): any { invariant(false, "abstract method; please override"); } + + getDebugName(): string | void { + return this.intrinsicName || this.__originalName; + } }