Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2
* vim: set ts=8 sts=2 et sw=2 tw=80:
3
* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "debugger/Source.h"
8
9
#include "mozilla/Assertions.h" // for AssertionConditionType, MOZ_ASSERT
10
#include "mozilla/Maybe.h" // for Some, Maybe, Nothing
11
#include "mozilla/Variant.h" // for AsVariant, Variant
12
13
#include <stdint.h> // for uint32_t
14
#include <string.h> // for memcpy
15
#include <utility> // for move
16
17
#include "jsapi.h" // for JS_ReportErrorNumberASCII, JS_CopyStringCharsZ
18
#include "jsfriendapi.h" // for GetErrorMessage, JS_NewUint8Array
19
20
#include "debugger/Debugger.h" // for DebuggerSourceReferent, Debugger
21
#include "debugger/Script.h" // for DebuggerScript
22
#include "gc/Tracer.h" // for TraceManuallyBarrieredCrossCompartmentEdge
23
#include "js/CompilationAndEvaluation.h" // for Compile
24
#include "vm/BytecodeUtil.h" // for JSDVG_SEARCH_STACK
25
#include "vm/JSContext.h" // for JSContext (ptr only)
26
#include "vm/JSObject.h" // for JSObject, RequireObject
27
#include "vm/JSScript.h" // for ScriptSource, ScriptSourceObject
28
#include "vm/ObjectGroup.h" // for TenuredObject
29
#include "vm/StringType.h" // for NewStringCopyZ, JSString (ptr only)
30
#include "vm/TypedArrayObject.h" // for TypedArrayObject, JSObject::is
31
#include "wasm/WasmCode.h" // for Metadata
32
#include "wasm/WasmDebug.h" // for DebugState
33
#include "wasm/WasmInstance.h" // for Instance
34
#include "wasm/WasmJS.h" // for WasmInstanceObject
35
#include "wasm/WasmTypes.h" // for Bytes, RootedWasmInstanceObject
36
37
#include "vm/JSObject-inl.h" // for InitClass
38
#include "vm/NativeObject-inl.h" // for NewNativeObjectWithGivenProto
39
40
namespace js {
41
class GlobalObject;
42
}
43
44
using namespace js;
45
46
using mozilla::AsVariant;
47
using mozilla::Maybe;
48
using mozilla::Nothing;
49
using mozilla::Some;
50
51
const JSClassOps DebuggerSource::classOps_ = {
52
nullptr, /* addProperty */
53
nullptr, /* delProperty */
54
nullptr, /* enumerate */
55
nullptr, /* newEnumerate */
56
nullptr, /* resolve */
57
nullptr, /* mayResolve */
58
nullptr, /* finalize */
59
nullptr, /* call */
60
nullptr, /* hasInstance */
61
nullptr, /* construct */
62
CallTraceMethod<DebuggerSource>, /* trace */
63
};
64
65
const JSClass DebuggerSource::class_ = {
66
"Source", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS),
67
&classOps_};
68
69
/* static */
70
NativeObject* DebuggerSource::initClass(JSContext* cx,
71
Handle<GlobalObject*> global,
72
HandleObject debugCtor) {
73
return InitClass(cx, debugCtor, nullptr, &class_, construct, 0, properties_,
74
methods_, nullptr, nullptr);
75
}
76
77
/* static */
78
DebuggerSource* DebuggerSource::create(JSContext* cx, HandleObject proto,
79
Handle<DebuggerSourceReferent> referent,
80
HandleNativeObject debugger) {
81
NativeObject* obj =
82
NewNativeObjectWithGivenProto(cx, &class_, proto, TenuredObject);
83
if (!obj) {
84
return nullptr;
85
}
86
RootedDebuggerSource sourceObj(cx, &obj->as<DebuggerSource>());
87
sourceObj->setReservedSlot(OWNER_SLOT, ObjectValue(*debugger));
88
referent.get().match(
89
[&](auto sourceHandle) { sourceObj->setPrivateGCThing(sourceHandle); });
90
91
return sourceObj;
92
}
93
94
// For internal use only.
95
NativeObject* DebuggerSource::getReferentRawObject() const {
96
return static_cast<NativeObject*>(getPrivate());
97
}
98
99
DebuggerSourceReferent DebuggerSource::getReferent() const {
100
if (NativeObject* referent = getReferentRawObject()) {
101
if (referent->is<ScriptSourceObject>()) {
102
return AsVariant(&referent->as<ScriptSourceObject>());
103
}
104
return AsVariant(&referent->as<WasmInstanceObject>());
105
}
106
return AsVariant(static_cast<ScriptSourceObject*>(nullptr));
107
}
108
109
void DebuggerSource::trace(JSTracer* trc) {
110
// There is a barrier on private pointers, so the Unbarriered marking
111
// is okay.
112
if (JSObject* referent = getReferentRawObject()) {
113
TraceManuallyBarrieredCrossCompartmentEdge(
114
trc, static_cast<JSObject*>(this), &referent,
115
"Debugger.Source referent");
116
setPrivateUnbarriered(referent);
117
}
118
}
119
120
/* static */
121
bool DebuggerSource::construct(JSContext* cx, unsigned argc, Value* vp) {
122
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
123
"Debugger.Source");
124
return false;
125
}
126
127
/* static */
128
DebuggerSource* DebuggerSource::check(JSContext* cx, HandleValue thisv) {
129
JSObject* thisobj = RequireObject(cx, thisv);
130
if (!thisobj) {
131
return nullptr;
132
}
133
if (!thisobj->is<DebuggerSource>()) {
134
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
135
JSMSG_INCOMPATIBLE_PROTO, "Debugger.Source",
136
"method", thisobj->getClass()->name);
137
return nullptr;
138
}
139
140
DebuggerSource* thisSourceObj = &thisobj->as<DebuggerSource>();
141
142
if (!thisSourceObj->getReferentRawObject()) {
143
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
144
JSMSG_INCOMPATIBLE_PROTO, "Debugger.Source",
145
"method", "prototype object");
146
return nullptr;
147
}
148
149
return thisSourceObj;
150
}
151
152
struct MOZ_STACK_CLASS DebuggerSource::CallData {
153
JSContext* cx;
154
const CallArgs& args;
155
156
HandleDebuggerSource obj;
157
Rooted<DebuggerSourceReferent> referent;
158
159
CallData(JSContext* cx, const CallArgs& args, HandleDebuggerSource obj)
160
: cx(cx), args(args), obj(obj), referent(cx, obj->getReferent()) {}
161
162
bool getText();
163
bool getBinary();
164
bool getURL();
165
bool getStartLine();
166
bool getId();
167
bool getDisplayURL();
168
bool getElement();
169
bool getElementProperty();
170
bool getIntroductionScript();
171
bool getIntroductionOffset();
172
bool getIntroductionType();
173
bool setSourceMapURL();
174
bool getSourceMapURL();
175
bool reparse();
176
177
using Method = bool (CallData::*)();
178
179
template <Method MyMethod>
180
static bool ToNative(JSContext* cx, unsigned argc, Value* vp);
181
};
182
183
template <DebuggerSource::CallData::Method MyMethod>
184
/* static */
185
bool DebuggerSource::CallData::ToNative(JSContext* cx, unsigned argc,
186
Value* vp) {
187
CallArgs args = CallArgsFromVp(argc, vp);
188
189
RootedDebuggerSource obj(cx, DebuggerSource::check(cx, args.thisv()));
190
if (!obj) {
191
return false;
192
}
193
194
CallData data(cx, args, obj);
195
return (data.*MyMethod)();
196
}
197
198
class DebuggerSourceGetTextMatcher {
199
JSContext* cx_;
200
201
public:
202
explicit DebuggerSourceGetTextMatcher(JSContext* cx) : cx_(cx) {}
203
204
using ReturnType = JSString*;
205
206
ReturnType match(HandleScriptSourceObject sourceObject) {
207
ScriptSource* ss = sourceObject->source();
208
bool hasSourceText;
209
if (!ScriptSource::loadSource(cx_, ss, &hasSourceText)) {
210
return nullptr;
211
}
212
if (!hasSourceText) {
213
return NewStringCopyZ<CanGC>(cx_, "[no source]");
214
}
215
216
if (ss->isFunctionBody()) {
217
return ss->functionBodyString(cx_);
218
}
219
220
return ss->substring(cx_, 0, ss->length());
221
}
222
223
ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
224
wasm::Instance& instance = instanceObj->instance();
225
const char* msg;
226
if (!instance.debugEnabled()) {
227
msg = "Restart with developer tools open to view WebAssembly source.";
228
} else {
229
msg = "[debugger missing wasm binary-to-text conversion]";
230
}
231
return NewStringCopyZ<CanGC>(cx_, msg);
232
}
233
};
234
235
bool DebuggerSource::CallData::getText() {
236
Value textv = obj->getReservedSlot(TEXT_SLOT);
237
if (!textv.isUndefined()) {
238
MOZ_ASSERT(textv.isString());
239
args.rval().set(textv);
240
return true;
241
}
242
243
DebuggerSourceGetTextMatcher matcher(cx);
244
JSString* str = referent.match(matcher);
245
if (!str) {
246
return false;
247
}
248
249
args.rval().setString(str);
250
obj->setReservedSlot(TEXT_SLOT, args.rval());
251
return true;
252
}
253
254
bool DebuggerSource::CallData::getBinary() {
255
if (!referent.is<WasmInstanceObject*>()) {
256
ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK,
257
args.thisv(), nullptr, "a wasm source");
258
return false;
259
}
260
261
RootedWasmInstanceObject instanceObj(cx, referent.as<WasmInstanceObject*>());
262
wasm::Instance& instance = instanceObj->instance();
263
264
if (!instance.debugEnabled()) {
265
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
266
JSMSG_DEBUG_NO_BINARY_SOURCE);
267
return false;
268
}
269
270
const wasm::Bytes& bytecode = instance.debug().bytecode();
271
RootedObject arr(cx, JS_NewUint8Array(cx, bytecode.length()));
272
if (!arr) {
273
return false;
274
}
275
276
memcpy(arr->as<TypedArrayObject>().dataPointerUnshared(), bytecode.begin(),
277
bytecode.length());
278
279
args.rval().setObject(*arr);
280
return true;
281
}
282
283
class DebuggerSourceGetURLMatcher {
284
JSContext* cx_;
285
286
public:
287
explicit DebuggerSourceGetURLMatcher(JSContext* cx) : cx_(cx) {}
288
289
using ReturnType = Maybe<JSString*>;
290
291
ReturnType match(HandleScriptSourceObject sourceObject) {
292
ScriptSource* ss = sourceObject->source();
293
MOZ_ASSERT(ss);
294
if (ss->filename()) {
295
JSString* str = NewStringCopyZ<CanGC>(cx_, ss->filename());
296
return Some(str);
297
}
298
return Nothing();
299
}
300
ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
301
return Some(instanceObj->instance().createDisplayURL(cx_));
302
}
303
};
304
305
bool DebuggerSource::CallData::getURL() {
306
DebuggerSourceGetURLMatcher matcher(cx);
307
Maybe<JSString*> str = referent.match(matcher);
308
if (str.isSome()) {
309
if (!*str) {
310
return false;
311
}
312
args.rval().setString(*str);
313
} else {
314
args.rval().setNull();
315
}
316
return true;
317
}
318
319
class DebuggerSourceGetStartLineMatcher {
320
public:
321
using ReturnType = uint32_t;
322
323
ReturnType match(HandleScriptSourceObject sourceObject) {
324
ScriptSource* ss = sourceObject->source();
325
return ss->startLine();
326
}
327
ReturnType match(Handle<WasmInstanceObject*> instanceObj) { return 0; }
328
};
329
330
bool DebuggerSource::CallData::getStartLine() {
331
DebuggerSourceGetStartLineMatcher matcher;
332
uint32_t line = referent.match(matcher);
333
args.rval().setNumber(line);
334
return true;
335
}
336
337
class DebuggerSourceGetIdMatcher {
338
public:
339
using ReturnType = uint32_t;
340
341
ReturnType match(HandleScriptSourceObject sourceObject) {
342
ScriptSource* ss = sourceObject->source();
343
return ss->id();
344
}
345
ReturnType match(Handle<WasmInstanceObject*> instanceObj) { return 0; }
346
};
347
348
bool DebuggerSource::CallData::getId() {
349
DebuggerSourceGetIdMatcher matcher;
350
uint32_t id = referent.match(matcher);
351
args.rval().setNumber(id);
352
return true;
353
}
354
355
struct DebuggerSourceGetDisplayURLMatcher {
356
using ReturnType = const char16_t*;
357
ReturnType match(HandleScriptSourceObject sourceObject) {
358
ScriptSource* ss = sourceObject->source();
359
MOZ_ASSERT(ss);
360
return ss->hasDisplayURL() ? ss->displayURL() : nullptr;
361
}
362
ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
363
return wasmInstance->instance().metadata().displayURL();
364
}
365
};
366
367
bool DebuggerSource::CallData::getDisplayURL() {
368
DebuggerSourceGetDisplayURLMatcher matcher;
369
if (const char16_t* displayURL = referent.match(matcher)) {
370
JSString* str = JS_NewUCStringCopyZ(cx, displayURL);
371
if (!str) {
372
return false;
373
}
374
args.rval().setString(str);
375
} else {
376
args.rval().setNull();
377
}
378
return true;
379
}
380
381
struct DebuggerSourceGetElementMatcher {
382
using ReturnType = JSObject*;
383
ReturnType match(HandleScriptSourceObject sourceObject) {
384
return sourceObject->unwrappedElement();
385
}
386
ReturnType match(Handle<WasmInstanceObject*> wasmInstance) { return nullptr; }
387
};
388
389
bool DebuggerSource::CallData::getElement() {
390
DebuggerSourceGetElementMatcher matcher;
391
if (JSObject* element = referent.match(matcher)) {
392
args.rval().setObjectOrNull(element);
393
if (!Debugger::fromChildJSObject(obj)->wrapDebuggeeValue(cx, args.rval())) {
394
return false;
395
}
396
} else {
397
args.rval().setUndefined();
398
}
399
return true;
400
}
401
402
struct DebuggerSourceGetElementPropertyMatcher {
403
using ReturnType = Value;
404
ReturnType match(HandleScriptSourceObject sourceObject) {
405
return sourceObject->unwrappedElementAttributeName();
406
}
407
ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
408
return UndefinedValue();
409
}
410
};
411
412
bool DebuggerSource::CallData::getElementProperty() {
413
DebuggerSourceGetElementPropertyMatcher matcher;
414
args.rval().set(referent.match(matcher));
415
return Debugger::fromChildJSObject(obj)->wrapDebuggeeValue(cx, args.rval());
416
}
417
418
class DebuggerSourceGetIntroductionScriptMatcher {
419
JSContext* cx_;
420
Debugger* dbg_;
421
MutableHandleValue rval_;
422
423
public:
424
DebuggerSourceGetIntroductionScriptMatcher(JSContext* cx, Debugger* dbg,
425
MutableHandleValue rval)
426
: cx_(cx), dbg_(dbg), rval_(rval) {}
427
428
using ReturnType = bool;
429
430
ReturnType match(HandleScriptSourceObject sourceObject) {
431
RootedScript script(cx_, sourceObject->unwrappedIntroductionScript());
432
if (script) {
433
RootedObject scriptDO(cx_, dbg_->wrapScript(cx_, script));
434
if (!scriptDO) {
435
return false;
436
}
437
rval_.setObject(*scriptDO);
438
} else {
439
rval_.setUndefined();
440
}
441
return true;
442
}
443
444
ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
445
RootedObject ds(cx_, dbg_->wrapWasmScript(cx_, wasmInstance));
446
if (!ds) {
447
return false;
448
}
449
rval_.setObject(*ds);
450
return true;
451
}
452
};
453
454
bool DebuggerSource::CallData::getIntroductionScript() {
455
Debugger* dbg = Debugger::fromChildJSObject(obj);
456
DebuggerSourceGetIntroductionScriptMatcher matcher(cx, dbg, args.rval());
457
return referent.match(matcher);
458
}
459
460
struct DebuggerGetIntroductionOffsetMatcher {
461
using ReturnType = Value;
462
ReturnType match(HandleScriptSourceObject sourceObject) {
463
// Regardless of what's recorded in the ScriptSourceObject and
464
// ScriptSource, only hand out the introduction offset if we also have
465
// the script within which it applies.
466
ScriptSource* ss = sourceObject->source();
467
if (ss->hasIntroductionOffset() &&
468
sourceObject->unwrappedIntroductionScript()) {
469
return Int32Value(ss->introductionOffset());
470
}
471
return UndefinedValue();
472
}
473
ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
474
return UndefinedValue();
475
}
476
};
477
478
bool DebuggerSource::CallData::getIntroductionOffset() {
479
DebuggerGetIntroductionOffsetMatcher matcher;
480
args.rval().set(referent.match(matcher));
481
return true;
482
}
483
484
struct DebuggerSourceGetIntroductionTypeMatcher {
485
using ReturnType = const char*;
486
ReturnType match(HandleScriptSourceObject sourceObject) {
487
ScriptSource* ss = sourceObject->source();
488
MOZ_ASSERT(ss);
489
return ss->hasIntroductionType() ? ss->introductionType() : nullptr;
490
}
491
ReturnType match(Handle<WasmInstanceObject*> wasmInstance) { return "wasm"; }
492
};
493
494
bool DebuggerSource::CallData::getIntroductionType() {
495
DebuggerSourceGetIntroductionTypeMatcher matcher;
496
if (const char* introductionType = referent.match(matcher)) {
497
JSString* str = NewStringCopyZ<CanGC>(cx, introductionType);
498
if (!str) {
499
return false;
500
}
501
args.rval().setString(str);
502
} else {
503
args.rval().setUndefined();
504
}
505
506
return true;
507
}
508
509
ScriptSourceObject* EnsureSourceObject(JSContext* cx,
510
HandleDebuggerSource obj) {
511
if (!obj->getReferent().is<ScriptSourceObject*>()) {
512
RootedValue v(cx, ObjectValue(*obj));
513
ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK, v,
514
nullptr, "a JS source");
515
return nullptr;
516
}
517
return obj->getReferent().as<ScriptSourceObject*>();
518
}
519
520
bool DebuggerSource::CallData::setSourceMapURL() {
521
RootedScriptSourceObject sourceObject(cx, EnsureSourceObject(cx, obj));
522
if (!sourceObject) {
523
return false;
524
}
525
ScriptSource* ss = sourceObject->source();
526
MOZ_ASSERT(ss);
527
528
if (!args.requireAtLeast(cx, "set sourceMapURL", 1)) {
529
return false;
530
}
531
532
JSString* str = ToString<CanGC>(cx, args[0]);
533
if (!str) {
534
return false;
535
}
536
537
UniqueTwoByteChars chars = JS_CopyStringCharsZ(cx, str);
538
if (!chars) {
539
return false;
540
}
541
542
if (!ss->setSourceMapURL(cx, std::move(chars))) {
543
return false;
544
}
545
546
args.rval().setUndefined();
547
return true;
548
}
549
550
class DebuggerSourceGetSourceMapURLMatcher {
551
JSContext* cx_;
552
MutableHandleString result_;
553
554
public:
555
explicit DebuggerSourceGetSourceMapURLMatcher(JSContext* cx,
556
MutableHandleString result)
557
: cx_(cx), result_(result) {}
558
559
using ReturnType = bool;
560
ReturnType match(HandleScriptSourceObject sourceObject) {
561
ScriptSource* ss = sourceObject->source();
562
MOZ_ASSERT(ss);
563
if (!ss->hasSourceMapURL()) {
564
result_.set(nullptr);
565
return true;
566
}
567
JSString* str = JS_NewUCStringCopyZ(cx_, ss->sourceMapURL());
568
if (!str) {
569
return false;
570
}
571
result_.set(str);
572
return true;
573
}
574
ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
575
wasm::Instance& instance = instanceObj->instance();
576
if (!instance.debugEnabled()) {
577
result_.set(nullptr);
578
return true;
579
}
580
581
RootedString str(cx_);
582
if (!instance.debug().getSourceMappingURL(cx_, &str)) {
583
return false;
584
}
585
586
result_.set(str);
587
return true;
588
}
589
};
590
591
bool DebuggerSource::CallData::getSourceMapURL() {
592
RootedString result(cx);
593
DebuggerSourceGetSourceMapURLMatcher matcher(cx, &result);
594
if (!referent.match(matcher)) {
595
return false;
596
}
597
if (result) {
598
args.rval().setString(result);
599
} else {
600
args.rval().setNull();
601
}
602
return true;
603
}
604
605
template <typename Unit>
606
static JSScript* ReparseSource(JSContext* cx, HandleScriptSourceObject sso) {
607
AutoRealm ar(cx, sso);
608
ScriptSource* ss = sso->source();
609
610
JS::CompileOptions options(cx);
611
options.hideScriptFromDebugger = true;
612
options.setFileAndLine(ss->filename(), ss->startLine());
613
614
UncompressedSourceCache::AutoHoldEntry holder;
615
616
ScriptSource::PinnedUnits<Unit> units(cx, ss, holder, 0, ss->length());
617
if (!units.get()) {
618
return nullptr;
619
}
620
621
JS::SourceText<Unit> srcBuf;
622
if (!srcBuf.init(cx, units.get(), ss->length(),
623
JS::SourceOwnership::Borrowed)) {
624
return nullptr;
625
}
626
627
return JS::Compile(cx, options, srcBuf);
628
}
629
630
bool DebuggerSource::CallData::reparse() {
631
RootedScriptSourceObject sourceObject(cx, EnsureSourceObject(cx, obj));
632
if (!sourceObject) {
633
return false;
634
}
635
636
if (!sourceObject->source()->hasSourceText()) {
637
JS_ReportErrorASCII(cx, "Source object missing text");
638
return false;
639
}
640
641
RootedScript script(cx);
642
if (sourceObject->source()->hasSourceType<mozilla::Utf8Unit>()) {
643
script = ReparseSource<mozilla::Utf8Unit>(cx, sourceObject);
644
} else {
645
script = ReparseSource<char16_t>(cx, sourceObject);
646
}
647
648
if (!script) {
649
return false;
650
}
651
652
Debugger* dbg = Debugger::fromChildJSObject(obj);
653
RootedObject scriptDO(cx, dbg->wrapScript(cx, script));
654
if (!scriptDO) {
655
return false;
656
}
657
658
args.rval().setObject(*scriptDO);
659
return true;
660
}
661
662
const JSPropertySpec DebuggerSource::properties_[] = {
663
JS_DEBUG_PSG("text", getText),
664
JS_DEBUG_PSG("binary", getBinary),
665
JS_DEBUG_PSG("url", getURL),
666
JS_DEBUG_PSG("startLine", getStartLine),
667
JS_DEBUG_PSG("id", getId),
668
JS_DEBUG_PSG("element", getElement),
669
JS_DEBUG_PSG("displayURL", getDisplayURL),
670
JS_DEBUG_PSG("introductionScript", getIntroductionScript),
671
JS_DEBUG_PSG("introductionOffset", getIntroductionOffset),
672
JS_DEBUG_PSG("introductionType", getIntroductionType),
673
JS_DEBUG_PSG("elementAttributeName", getElementProperty),
674
JS_DEBUG_PSGS("sourceMapURL", getSourceMapURL, setSourceMapURL),
675
JS_PS_END};
676
677
const JSFunctionSpec DebuggerSource::methods_[] = {
678
JS_DEBUG_FN("reparse", reparse, 0), JS_FS_END};