source: webkit/trunk/JavaScriptCore/VM/CodeBlock.cpp@ 34157

Last change on this file since 34157 was 34157, checked in by Adam Roben, 17 years ago

Roll out r34156, which was accidentally checked in

File size: 22.4 KB
Line 
1/*
2 * Copyright (C) 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 Cameron Zwarich <[email protected]>
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 * its contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include "config.h"
31#include "CodeBlock.h"
32
33#include "Machine.h"
34#include "debugger.h"
35#include "value.h"
36#include <stdio.h>
37
38namespace KJS {
39
40static UString escapeQuotes(const UString& str)
41{
42 UString result = str;
43 int pos = 0;
44 while ((pos = result.find('\"', pos)) >= 0) {
45 result = result.substr(0, pos) + "\"\\\"\"" + result.substr(pos + 1);
46 pos += 4;
47 }
48 return result;
49}
50
51static UString valueToSourceString(ExecState* exec, JSValue* val)
52{
53 if (val->isString()) {
54 UString result("\"");
55 result += escapeQuotes(val->toString(exec)) + "\"";
56 return result;
57 }
58
59 return val->toString(exec);
60}
61
62static CString registerName(int r)
63{
64 if (r < 0)
65 return (UString("lr") + UString::from(-r)).UTF8String();
66
67 return (UString("tr") + UString::from(r)).UTF8String();
68}
69
70static CString constantName(ExecState* exec, int k, JSValue* value)
71{
72 return (valueToSourceString(exec, value) + "(@k" + UString::from(k) + ")").UTF8String();
73}
74
75static CString idName(int id0, const Identifier& ident)
76{
77 return (ident.ustring() + "(@id" + UString::from(id0) +")").UTF8String();
78}
79
80static UString regexpToSourceString(RegExp* regExp)
81{
82 UString pattern = UString("/") + regExp->pattern() + "/";
83 if (regExp->global())
84 pattern += "g";
85 if (regExp->ignoreCase())
86 pattern += "i";
87 if (regExp->multiline())
88 pattern += "m";
89
90 return pattern;
91}
92
93static CString regexpName(int re, RegExp* regexp)
94{
95 return (regexpToSourceString(regexp) + "(@re" + UString::from(re) + ")").UTF8String();
96}
97
98static const char* debugHookName(int debugHookID)
99{
100 if (debugHookID == DidEnterCallFrame)
101 return "didEnterCallFrame";
102 else if (debugHookID == WillLeaveCallFrame)
103 return "willLeaveCallFrame";
104 else {
105 ASSERT(debugHookID == WillExecuteStatement);
106 return "willExecuteStatement";
107 }
108}
109
110static int jumpTarget(const Vector<Instruction>::const_iterator& begin, Vector<Instruction>::const_iterator& it, int offset)
111{
112 return it - begin + offset;
113}
114
115static void printUnaryOp(int location, Vector<Instruction>::const_iterator& it, const char* op)
116{
117 int r0 = (++it)->u.operand;
118 int r1 = (++it)->u.operand;
119
120 printf("[%4d] %s\t\t %s, %s\n", location, op, registerName(r0).c_str(), registerName(r1).c_str());
121}
122
123static void printBinaryOp(int location, Vector<Instruction>::const_iterator& it, const char* op)
124{
125 int r0 = (++it)->u.operand;
126 int r1 = (++it)->u.operand;
127 int r2 = (++it)->u.operand;
128 printf("[%4d] %s\t\t %s, %s, %s\n", location, op, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str());
129}
130
131static void printConditionalJump(const Vector<Instruction>::const_iterator& begin, Vector<Instruction>::const_iterator& it, int location, const char* op)
132{
133 int r0 = (++it)->u.operand;
134 int offset = (++it)->u.operand;
135 printf("[%4d] %s\t\t %s, %d(->%d)\n", location, op, registerName(r0).c_str(), offset, jumpTarget(begin, it, offset));
136}
137
138void CodeBlock::dump(ExecState* exec) const
139{
140 Vector<Instruction>::const_iterator begin = instructions.begin();
141 Vector<Instruction>::const_iterator end = instructions.end();
142
143 size_t instructionCount = 0;
144 for (Vector<Instruction>::const_iterator it = begin; it != end; ++it)
145 if (machine().isOpcode(it->u.opcode))
146 ++instructionCount;
147
148 printf("%lu instructions; %lu bytes at %p; %d locals (%d parameters); %d temporaries\n\n", instructionCount, instructions.size() * sizeof(Instruction), this, numLocals, numParameters, numTemporaries);
149
150 for (Vector<Instruction>::const_iterator it = begin; it != end; ++it)
151 dump(exec, begin, it);
152
153 if (identifiers.size()) {
154 printf("\nIdentifiers:\n");
155 size_t i = 0;
156 do {
157 printf(" id%u = %s\n", static_cast<unsigned>(i), identifiers[i].ascii());
158 ++i;
159 } while (i != identifiers.size());
160 }
161
162 if (jsValues.size()) {
163 printf("\nConstants:\n");
164 size_t i = 0;
165 do {
166 printf(" k%u = %s\n", static_cast<unsigned>(i), valueToSourceString(exec, jsValues[i]).ascii());
167 ++i;
168 } while (i < jsValues.size());
169 }
170
171 if (regexps.size()) {
172 printf("\nRegExps:\n");
173 size_t i = 0;
174 do {
175 printf(" re%u = %s\n", static_cast<unsigned>(i), regexpToSourceString(regexps[i].get()).ascii());
176 ++i;
177 } while (i < regexps.size());
178 }
179
180 if (exceptionHandlers.size()) {
181 printf("\nException Handlers:\n");
182 unsigned i = 0;
183 do {
184 printf("\t %d: { start: [%4d] end: [%4d] target: [%4d] }\n", i+1, exceptionHandlers[i].start, exceptionHandlers[i].end, exceptionHandlers[i].target);
185 ++i;
186 } while (i < exceptionHandlers.size());
187 }
188
189 printf("\n");
190}
191
192void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& begin, Vector<Instruction>::const_iterator& it) const
193{
194 int location = it - begin;
195 switch (machine().getOpcodeID(it->u.opcode)) {
196 case op_load: {
197 int r0 = (++it)->u.operand;
198 int k0 = (++it)->u.operand;
199 printf("[%4d] load\t\t %s, %s\t\t\n", location, registerName(r0).c_str(), constantName(exec, k0, jsValues[k0]).c_str());
200 break;
201 }
202 case op_new_object: {
203 int r0 = (++it)->u.operand;
204 printf("[%4d] new_object\t %s\n", location, registerName(r0).c_str());
205 break;
206 }
207 case op_new_array: {
208 int r0 = (++it)->u.operand;
209 printf("[%4d] new_array\t %s\n", location, registerName(r0).c_str());
210 break;
211 }
212 case op_new_regexp: {
213 int r0 = (++it)->u.operand;
214 int re0 = (++it)->u.operand;
215 printf("[%4d] new_regexp\t %s, %s\n", location, registerName(r0).c_str(), regexpName(re0, regexps[re0].get()).c_str());
216 break;
217 }
218 case op_mov: {
219 int r0 = (++it)->u.operand;
220 int r1 = (++it)->u.operand;
221 printf("[%4d] mov\t\t %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str());
222 break;
223 }
224 case op_not: {
225 printUnaryOp(location, it, "not");
226 break;
227 }
228 case op_eq: {
229 printBinaryOp(location, it, "eq");
230 break;
231 }
232 case op_neq: {
233 printBinaryOp(location, it, "neq");
234 break;
235 }
236 case op_stricteq: {
237 printBinaryOp(location, it, "stricteq");
238 break;
239 }
240 case op_nstricteq: {
241 printBinaryOp(location, it, "nstricteq");
242 break;
243 }
244 case op_less: {
245 printBinaryOp(location, it, "less");
246 break;
247 }
248 case op_lesseq: {
249 printBinaryOp(location, it, "lesseq");
250 break;
251 }
252 case op_pre_inc: {
253 int r0 = (++it)->u.operand;
254 printf("[%4d] pre_inc\t\t %s\n", location, registerName(r0).c_str());
255 break;
256 }
257 case op_pre_dec: {
258 int r0 = (++it)->u.operand;
259 printf("[%4d] pre_dec\t\t %s\n", location, registerName(r0).c_str());
260 break;
261 }
262 case op_post_inc: {
263 printUnaryOp(location, it, "post_inc");
264 break;
265 }
266 case op_post_dec: {
267 printUnaryOp(location, it, "post_dec");
268 break;
269 }
270 case op_to_jsnumber: {
271 printUnaryOp(location, it, "to_jsnumber");
272 break;
273 }
274 case op_negate: {
275 printUnaryOp(location, it, "negate");
276 break;
277 }
278 case op_add: {
279 printBinaryOp(location, it, "add");
280 break;
281 }
282 case op_mul: {
283 printBinaryOp(location, it, "mul");
284 break;
285 }
286 case op_div: {
287 printBinaryOp(location, it, "div");
288 break;
289 }
290 case op_mod: {
291 printBinaryOp(location, it, "mod");
292 break;
293 }
294 case op_sub: {
295 printBinaryOp(location, it, "sub");
296 break;
297 }
298 case op_lshift: {
299 printBinaryOp(location, it, "lshift");
300 break;
301 }
302 case op_rshift: {
303 printBinaryOp(location, it, "rshift");
304 break;
305 }
306 case op_urshift: {
307 printBinaryOp(location, it, "urshift");
308 break;
309 }
310 case op_bitand: {
311 printBinaryOp(location, it, "bitand");
312 break;
313 }
314 case op_bitxor: {
315 printBinaryOp(location, it, "bitxor");
316 break;
317 }
318 case op_bitor: {
319 printBinaryOp(location, it, "bitor");
320 break;
321 }
322 case op_bitnot: {
323 printUnaryOp(location, it, "bitnot");
324 break;
325 }
326 case op_instanceof: {
327 printBinaryOp(location, it, "instanceof");
328 break;
329 }
330 case op_typeof: {
331 printUnaryOp(location, it, "typeof");
332 break;
333 }
334 case op_in: {
335 printBinaryOp(location, it, "in");
336 break;
337 }
338 case op_resolve: {
339 int r0 = (++it)->u.operand;
340 int id0 = (++it)->u.operand;
341 printf("[%4d] resolve\t\t %s, %s\n", location, registerName(r0).c_str(), idName(id0, identifiers[id0]).c_str());
342 break;
343 }
344 case op_resolve_skip: {
345 int r0 = (++it)->u.operand;
346 int id0 = (++it)->u.operand;
347 int skipLevels = (++it)->u.operand;
348 printf("[%4d] resolve_skip\t %s, %s, %d\n", location, registerName(r0).c_str(), idName(id0, identifiers[id0]).c_str(), skipLevels);
349 break;
350 }
351 case op_get_scoped_var: {
352 int r0 = (++it)->u.operand;
353 int index = (++it)->u.operand;
354 int skipLevels = (++it)->u.operand;
355 printf("[%4d] get_scoped_var\t\t %s, %d, %d\n", location, registerName(r0).c_str(), index, skipLevels);
356 break;
357 }
358 case op_put_scoped_var: {
359 int index = (++it)->u.operand;
360 int skipLevels = (++it)->u.operand;
361 int r0 = (++it)->u.operand;
362 printf("[%4d] put_scoped_var\t\t %d, %d, %s\n", location, index, skipLevels, registerName(r0).c_str());
363 break;
364 }
365 case op_resolve_base: {
366 int r0 = (++it)->u.operand;
367 int id0 = (++it)->u.operand;
368 printf("[%4d] resolve_base\t %s, %s\n", location, registerName(r0).c_str(), idName(id0, identifiers[id0]).c_str());
369 break;
370 }
371 case op_resolve_with_base: {
372 int r0 = (++it)->u.operand;
373 int r1 = (++it)->u.operand;
374 int id0 = (++it)->u.operand;
375 printf("[%4d] resolve_with_base %s, %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str(), idName(id0, identifiers[id0]).c_str());
376 break;
377 }
378 case op_resolve_func: {
379 int r0 = (++it)->u.operand;
380 int r1 = (++it)->u.operand;
381 int id0 = (++it)->u.operand;
382 printf("[%4d] resolve_func\t %s, %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str(), idName(id0, identifiers[id0]).c_str());
383 break;
384 }
385 case op_get_by_id: {
386 int r0 = (++it)->u.operand;
387 int r1 = (++it)->u.operand;
388 int id0 = (++it)->u.operand;
389 printf("[%4d] get_by_id\t %s, %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str(), idName(id0, identifiers[id0]).c_str());
390 break;
391 }
392 case op_put_by_id: {
393 int r0 = (++it)->u.operand;
394 int id0 = (++it)->u.operand;
395 int r1 = (++it)->u.operand;
396 printf("[%4d] put_by_id\t %s, %s, %s\n", location, registerName(r0).c_str(), idName(id0, identifiers[id0]).c_str(), registerName(r1).c_str());
397 break;
398 }
399 case op_put_getter: {
400 int r0 = (++it)->u.operand;
401 int id0 = (++it)->u.operand;
402 int r1 = (++it)->u.operand;
403 printf("[%4d] put_getter\t %s, %s, %s\n", location, registerName(r0).c_str(), idName(id0, identifiers[id0]).c_str(), registerName(r1).c_str());
404 break;
405 }
406 case op_put_setter: {
407 int r0 = (++it)->u.operand;
408 int id0 = (++it)->u.operand;
409 int r1 = (++it)->u.operand;
410 printf("[%4d] put_setter\t %s, %s, %s\n", location, registerName(r0).c_str(), idName(id0, identifiers[id0]).c_str(), registerName(r1).c_str());
411 break;
412 }
413 case op_del_by_id: {
414 int r0 = (++it)->u.operand;
415 int r1 = (++it)->u.operand;
416 int id0 = (++it)->u.operand;
417 printf("[%4d] del_by_id\t %s, %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str(), idName(id0, identifiers[id0]).c_str());
418 break;
419 }
420 case op_get_by_val: {
421 int r0 = (++it)->u.operand;
422 int r1 = (++it)->u.operand;
423 int r2 = (++it)->u.operand;
424 printf("[%4d] get_by_val\t %s, %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str());
425 break;
426 }
427 case op_put_by_val: {
428 int r0 = (++it)->u.operand;
429 int r1 = (++it)->u.operand;
430 int r2 = (++it)->u.operand;
431 printf("[%4d] put_by_val\t %s, %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str());
432 break;
433 }
434 case op_del_by_val: {
435 int r0 = (++it)->u.operand;
436 int r1 = (++it)->u.operand;
437 int r2 = (++it)->u.operand;
438 printf("[%4d] del_by_val\t %s, %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str());
439 break;
440 }
441 case op_put_by_index: {
442 int r0 = (++it)->u.operand;
443 unsigned n0 = (++it)->u.operand;
444 int r1 = (++it)->u.operand;
445 printf("[%4d] put_by_index\t %s, %u, %s\n", location, registerName(r0).c_str(), n0, registerName(r1).c_str());
446 break;
447 }
448 case op_jmp: {
449 int offset = (++it)->u.operand;
450 printf("[%4d] jmp\t\t %d(->%d)\n", location, offset, jumpTarget(begin, it, offset));
451 break;
452 }
453 case op_jtrue: {
454 printConditionalJump(begin, it, location, "jtrue");
455 break;
456 }
457 case op_jfalse: {
458 printConditionalJump(begin, it, location, "jfalse");
459 break;
460 }
461 case op_new_func: {
462 int r0 = (++it)->u.operand;
463 int f0 = (++it)->u.operand;
464 printf("[%4d] new_func\t\t %s, f%d\n", location, registerName(r0).c_str(), f0);
465 break;
466 }
467 case op_new_func_exp: {
468 int r0 = (++it)->u.operand;
469 int f0 = (++it)->u.operand;
470 printf("[%4d] new_func_exp\t %s, f%d\n", location, registerName(r0).c_str(), f0);
471 break;
472 }
473 case op_call: {
474 int r0 = (++it)->u.operand;
475 int r1 = (++it)->u.operand;
476 int r2 = (++it)->u.operand;
477 int tempCount = (++it)->u.operand;
478 int argCount = (++it)->u.operand;
479 printf("[%4d] call\t\t %s, %s, %s, %d, %d\n", location, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str(), tempCount, argCount);
480 break;
481 }
482 case op_call_eval: {
483 int r0 = (++it)->u.operand;
484 int r1 = (++it)->u.operand;
485 int r2 = (++it)->u.operand;
486 int tempCount = (++it)->u.operand;
487 int argCount = (++it)->u.operand;
488 printf("[%4d] call_eval\t\t %s, %s, %s, %d, %d\n", location, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str(), tempCount, argCount);
489 break;
490 }
491 case op_ret: {
492 int r0 = (++it)->u.operand;
493 printf("[%4d] ret\t\t %s\n", location, registerName(r0).c_str());
494 break;
495 }
496 case op_construct: {
497 int r0 = (++it)->u.operand;
498 int r1 = (++it)->u.operand;
499 int tempCount = (++it)->u.operand;
500 int argCount = (++it)->u.operand;
501 printf("[%4d] construct\t %s, %s, %d, %d\n", location, registerName(r0).c_str(), registerName(r1).c_str(), tempCount, argCount);
502 break;
503 }
504 case op_get_pnames: {
505 int r0 = (++it)->u.operand;
506 int r1 = (++it)->u.operand;
507 printf("[%4d] get_pnames\t %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str());
508 break;
509 }
510 case op_next_pname: {
511 int dest = (++it)->u.operand;
512 int iter = (++it)->u.operand;
513 int offset = (++it)->u.operand;
514 printf("[%4d] next_pname\t %s, %s, %d(->%d)\n", location, registerName(dest).c_str(), registerName(iter).c_str(), offset, jumpTarget(begin, it, offset));
515 break;
516 }
517 case op_push_scope: {
518 int r0 = (++it)->u.operand;
519 printf("[%4d] push_scope\t %s\n", location, registerName(r0).c_str());
520 break;
521 }
522 case op_pop_scope: {
523 printf("[%4d] pop_scope\n", location);
524 break;
525 }
526 case op_jmp_scopes: {
527 int scopeDelta = (++it)->u.operand;
528 int offset = (++it)->u.operand;
529 printf("[%4d] jmp_scopes\t^%d, %d(->%d)\n", location, scopeDelta, offset, jumpTarget(begin, it, offset));
530 break;
531 }
532 case op_catch: {
533 int r0 = (++it)->u.operand;
534 printf("[%4d] catch\t\t %s\n", location, registerName(r0).c_str());
535 break;
536 }
537 case op_throw: {
538 int r0 = (++it)->u.operand;
539 printf("[%4d] throw\t\t %s\n", location, registerName(r0).c_str());
540 break;
541 }
542 case op_new_error: {
543 int r0 = (++it)->u.operand;
544 int errorType = (++it)->u.operand;
545 int k0 = (++it)->u.operand;
546 printf("[%4d] new_error\t %s, %d, %s\n", location, registerName(r0).c_str(), errorType, constantName(exec, k0, jsValues[k0]).c_str());
547 break;
548 }
549 case op_jsr: {
550 int retAddrDst = (++it)->u.operand;
551 int offset = (++it)->u.operand;
552 printf("[%4d] jsr\t\t %s, %d(->%d)\n", location, registerName(retAddrDst).c_str(), offset, jumpTarget(begin, it, offset));
553 break;
554 }
555 case op_sret: {
556 int retAddrSrc = (++it)->u.operand;
557 printf("[%4d] sret\t\t %s\n", location, registerName(retAddrSrc).c_str());
558 break;
559 }
560 case op_debug: {
561 int debugHookID = (++it)->u.operand;
562 int firstLine = (++it)->u.operand;
563 int lastLine = (++it)->u.operand;
564 printf("[%4d] debug\t\t %s, %d, %d\n", location, debugHookName(debugHookID), firstLine, lastLine);
565 break;
566 }
567 case op_end: {
568 int r0 = (++it)->u.operand;
569 printf("[%4d] end\t\t %s\n", location, registerName(r0).c_str());
570 break;
571 }
572 default: {
573 ASSERT_NOT_REACHED();
574 break;
575 }
576 }
577}
578
579void CodeBlock::mark()
580{
581 for (size_t i = 0; i < jsValues.size(); ++i)
582 if (!jsValues[i]->marked())
583 jsValues[i]->mark();
584
585 for (size_t i = 0; i < functions.size(); ++i)
586 functions[i]->body()->mark();
587
588 for (size_t i = 0; i < functionExpressions.size(); ++i)
589 functionExpressions[i]->body()->mark();
590}
591
592bool CodeBlock::getHandlerForVPC(const Instruction* vPC, Instruction*& target, int& scopeDepth)
593{
594 Vector<HandlerInfo>::iterator ptr = exceptionHandlers.begin();
595 Vector<HandlerInfo>::iterator end = exceptionHandlers.end();
596 unsigned addressOffset = vPC - instructions.begin();
597 ASSERT(addressOffset < instructions.size());
598
599 for (; ptr != end; ++ptr) {
600 // Handlers are ordered innermost first, so the first handler we encounter
601 // that contains the source address is the correct handler to use.
602 if (ptr->start <= addressOffset && ptr->end >= addressOffset) {
603 scopeDepth = ptr->scopeDepth;
604 target = instructions.begin() + ptr->target;
605 return true;
606 }
607 }
608 return false;
609}
610
611int CodeBlock::lineNumberForVPC(const Instruction* vPC)
612{
613 unsigned instructionOffset = vPC - instructions.begin();
614 ASSERT(instructionOffset < instructions.size());
615
616 if (!lineInfo.size())
617 return 1; // Empty function
618
619 int low = 0;
620 int high = lineInfo.size();
621 while (low < high) {
622 int mid = low + (high - low) / 2;
623 if (lineInfo[mid].instructionOffset <= instructionOffset)
624 low = mid + 1;
625 else
626 high = mid;
627 }
628
629 return lineInfo[low - 1].lineNumber;
630}
631
632} // namespace KJS
Note: See TracBrowser for help on using the repository browser.