summaryrefslogtreecommitdiffstats
path: root/js/src/tests/shell/script-file-name-utf8.js
blob: 99fb318ae2daca1dc207c5dba80d1af365e1597b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
// |reftest| skip-if(!xulRuntime.shell)

// Retrieve the script file name through various functions and ensure it's
// always correctly decoded from UTF-8.

// Special value when filename cannot be retrieved.
const NOT_SUPPORTED = "*not supported*";

// Return the file name from the Error#fileName property.
function errorFileName(fileName) {
  return evaluate("new Error().fileName", {fileName});
}

// Return the file name from the Parser by a SyntaxError.
function errorFileNameParser(fileName) {
  try {
    evaluate("###", {fileName});
  } catch (e) {
    return e.fileName;
  }
}

// Retrieve the file name through DescriptedCaller (1).
function descriptedCallerViaThisFileName(fileName) {
  return evaluate("thisFilename()", {fileName});
}

// Retrieve the file name through DescriptedCaller (2).
function descriptedCallerViaEvalInContext(fileName) {
  return evaluate("evalcx('new Error().fileName')", {fileName});
}

// Retrieve the file name through DescriptedCaller (3).
function descriptedCallerViaEval(fileName) {
  var pattern = / line 1 > eval$/;
  return evaluate("eval('new Error().fileName')", {fileName}).replace(pattern, "");
}

// Retrieve the file name through DescriptedCaller (4).
function descriptedCallerViaFunction(fileName) {
  var pattern = / line 1 > Function$/;
  return evaluate("Function('return new Error().fileName')()", {fileName}).replace(pattern, "");
}

// Retrieve the file name through DescriptedCaller (5).
function descriptedCallerViaEvalReturningScope(fileName) {
  return evaluate("evalReturningScope('var a = new Error().fileName')", {fileName}).a;
}

// Retrieve the file name through DescriptedCaller (7).
var wasmModuleConstructorTemp;
function descriptedCallerViaWasm(fileName) {
  if (!wasmIsSupported()) {
    return fileName;
  }

  wasmModuleConstructorTemp = null;
  evaluate(`
    function wasmEvalText(str, imports) {
      let binary = wasmTextToBinary(str);
      assertEq(WebAssembly.validate(binary), true);
      let m = new WebAssembly.Module(binary);
      return new WebAssembly.Instance(m, imports);
    }
    wasmEvalText('(module (import "" "a" (func)) (func (call 0)) (export "bar" (func 1)))',
                  {
                    "": {
                      a() {
                        wasmModuleConstructorTemp = new Error().stack;
                        return 0;
                      }
                    }
                  }).exports.bar();
  `, {fileName});
  var pattern = /^@(.*) line \d+ >.*$/;
  var index = 1; // Direct caller is the wasm function.
  return wasmModuleConstructorTemp.split("\n")[index].replace(pattern, "$1");
}

// Return the file name from Reflect.parse().
function reflectParseSource(fileName) {
  return Reflect.parse("", {source: fileName}).loc.source;
}

// Return the file name using the Error#stack property.
function fromErrorStack(fileName) {
  var pattern = /^@(.*):\d+:\d+$/;
  return evaluate("new Error().stack", {fileName}).split("\n")[0].replace(pattern, "$1");
}

// Return the file name using the Error#stack property from an asm.js function.
function fromErrorStackAsmJS(fileName) {
  var asm = evaluate(`(function asm(stdlib, foreign) {
    "use asm";
    var f = foreign.f;
    function g() {
      return f() | 0;
    }
    return {g: g};
  })`, {fileName});

  var stackFileName;
  var foreign = {
    f() {
      var pattern = /^g@(.*):\d+:\d+$/;
      var index = 1; // Direct caller is the asm.js function.
      var stack = new Error().stack;
      stackFileName = stack.split("\n")[index].replace(pattern, "$1");
      return 0;
    }
  };

  asm(this, foreign).g();

  return stackFileName;
}

// Return the file name using the Error#stacl property when a streaming compiled WASM function.
function fromErrorStackStreamingWasm(fileName) {
  if (!wasmIsSupported() || helperThreadCount() == 0) {
    return fileName;
  }

  var source = new Uint8Array(wasmTextToBinary(`
    (module (import "" "a" (func)) (func (call 0)) (export "bar" (func 1)))
  `));
  source.url = fileName;

  var stackFileName;
  var imports = {
    "": {
      a() {
        var pattern = /^@(.*):wasm-function.*$/;
        var index = 1; // Direct caller is the asm.js function.
        var stack = new Error().stack;
        stackFileName = stack.split("\n")[index].replace(pattern, "$1");
        return 0;
      }
    }
  };

  var result;
  WebAssembly.instantiateStreaming(source, imports).then(r => result = r);

  drainJobQueue();

  result.instance.exports.bar();

  return stackFileName;
}

// Return the file name using the embedded info in getBacktrace().
function getBacktraceScriptName(fileName) {
  var pattern = /^\d+ <TOP LEVEL> \["(.*)":\d+:\d\]$/;
  return evaluate("getBacktrace()", {fileName}).split("\n")[0].replace(pattern, "$1");
}

// Return the file name from the coverage report.
function getLcovInfoScriptName(fileName) {
  var g = newGlobal();
  var scriptFiles =  g.evaluate("getLcovInfo()", {fileName})
                      .split("\n")
                      .filter(x => x.startsWith("SF:"));
  assertEq(scriptFiles.length, 1);
  return scriptFiles[0].substring(3);
}

// Return the file name from the error during module import.
function moduleResolutionError(fileName) {
  const a = parseModule(`import { x } from "b";`, fileName);
  const ma = registerModule("a", a);
  const b = parseModule(`export var y = 10;`);
  const mb = registerModule("b", b);

  try {
    moduleLink(ma);
  } catch (e) {
    return e.fileName;
  }
}

// Return the file name from the profiler stack.
function geckoInterpProfilingStack(fileName) {
  enableGeckoProfilingWithSlowAssertions();
  const stack = evaluate(`readGeckoInterpProfilingStack();`, { fileName });
  if (stack.length === 0) {
    return NOT_SUPPORTED;
  }
  const result = stack[0].dynamicString;
  disableGeckoProfiling();
  return result;
}

const testFunctions = [
  errorFileName,
  errorFileNameParser,
  descriptedCallerViaThisFileName,
  descriptedCallerViaEvalInContext,
  descriptedCallerViaEval,
  descriptedCallerViaFunction,
  descriptedCallerViaEvalReturningScope,
  descriptedCallerViaWasm,
  reflectParseSource,
  fromErrorStack,
  fromErrorStackAsmJS,
  fromErrorStackStreamingWasm,
  getBacktraceScriptName,
  moduleResolutionError,
];

if (isLcovEnabled()) {
  testFunctions.push(getLcovInfoScriptName);
}

const fileNames = [
  "",
  "test",
  "Ðëßþ",
  "тест",
  "テスト",
  "\u{1F9EA}",
];

for (const fn of testFunctions) {
  for (const fileName of fileNames) {
    const result = fn(fileName);
    if (result === NOT_SUPPORTED) {
      continue;
    }
    assertEq(result, fileName, `Caller '${fn.name}'`);
  }
}

if (typeof reportCompare === "function")
  reportCompare(true, true);