diff options
Diffstat (limited to 'lcode.c')
-rw-r--r-- | lcode.c | 290 |
1 files changed, 241 insertions, 49 deletions
@@ -5,6 +5,8 @@ | |||
5 | */ | 5 | */ |
6 | 6 | ||
7 | 7 | ||
8 | #define LUA_REENTRANT | ||
9 | |||
8 | #include "lcode.h" | 10 | #include "lcode.h" |
9 | #include "ldo.h" | 11 | #include "ldo.h" |
10 | #include "llex.h" | 12 | #include "llex.h" |
@@ -47,9 +49,9 @@ int luaK_primitivecode (LexState *ls, Instruction i) { | |||
47 | static void luaK_minus (LexState *ls) { | 49 | static void luaK_minus (LexState *ls) { |
48 | Instruction *previous = previous_instruction(ls); | 50 | Instruction *previous = previous_instruction(ls); |
49 | switch(GET_OPCODE(*previous)) { | 51 | switch(GET_OPCODE(*previous)) { |
50 | case PUSHINT: *previous = SETARG_S(*previous, -GETARG_S(*previous)); return; | 52 | case PUSHINT: SETARG_S(*previous, -GETARG_S(*previous)); return; |
51 | case PUSHNUM: *previous = SET_OPCODE(*previous, PUSHNEGNUM); return; | 53 | case PUSHNUM: SET_OPCODE(*previous, PUSHNEGNUM); return; |
52 | case PUSHNEGNUM: *previous = SET_OPCODE(*previous, PUSHNUM); return; | 54 | case PUSHNEGNUM: SET_OPCODE(*previous, PUSHNUM); return; |
53 | default: luaK_primitivecode(ls, CREATE_0(MINUSOP)); | 55 | default: luaK_primitivecode(ls, CREATE_0(MINUSOP)); |
54 | } | 56 | } |
55 | } | 57 | } |
@@ -59,7 +61,7 @@ static void luaK_gettable (LexState *ls) { | |||
59 | Instruction *previous = previous_instruction(ls); | 61 | Instruction *previous = previous_instruction(ls); |
60 | luaK_deltastack(ls, -1); | 62 | luaK_deltastack(ls, -1); |
61 | switch(GET_OPCODE(*previous)) { | 63 | switch(GET_OPCODE(*previous)) { |
62 | case PUSHSTRING: *previous = SET_OPCODE(*previous, GETDOTTED); break; | 64 | case PUSHSTRING: SET_OPCODE(*previous, GETDOTTED); break; |
63 | default: luaK_primitivecode(ls, CREATE_0(GETTABLE)); | 65 | default: luaK_primitivecode(ls, CREATE_0(GETTABLE)); |
64 | } | 66 | } |
65 | } | 67 | } |
@@ -69,7 +71,7 @@ static void luaK_add (LexState *ls) { | |||
69 | Instruction *previous = previous_instruction(ls); | 71 | Instruction *previous = previous_instruction(ls); |
70 | luaK_deltastack(ls, -1); | 72 | luaK_deltastack(ls, -1); |
71 | switch(GET_OPCODE(*previous)) { | 73 | switch(GET_OPCODE(*previous)) { |
72 | case PUSHINT: *previous = SET_OPCODE(*previous, ADDI); break; | 74 | case PUSHINT: SET_OPCODE(*previous, ADDI); break; |
73 | default: luaK_primitivecode(ls, CREATE_0(ADDOP)); | 75 | default: luaK_primitivecode(ls, CREATE_0(ADDOP)); |
74 | } | 76 | } |
75 | } | 77 | } |
@@ -80,8 +82,8 @@ static void luaK_sub (LexState *ls) { | |||
80 | luaK_deltastack(ls, -1); | 82 | luaK_deltastack(ls, -1); |
81 | switch(GET_OPCODE(*previous)) { | 83 | switch(GET_OPCODE(*previous)) { |
82 | case PUSHINT: | 84 | case PUSHINT: |
83 | *previous = SET_OPCODE(*previous, ADDI); | 85 | SET_OPCODE(*previous, ADDI); |
84 | *previous = SETARG_S(*previous, -GETARG_S(*previous)); | 86 | SETARG_S(*previous, -GETARG_S(*previous)); |
85 | break; | 87 | break; |
86 | default: luaK_primitivecode(ls, CREATE_0(SUBOP)); | 88 | default: luaK_primitivecode(ls, CREATE_0(SUBOP)); |
87 | } | 89 | } |
@@ -92,7 +94,7 @@ static void luaK_conc (LexState *ls) { | |||
92 | Instruction *previous = previous_instruction(ls); | 94 | Instruction *previous = previous_instruction(ls); |
93 | luaK_deltastack(ls, -1); | 95 | luaK_deltastack(ls, -1); |
94 | switch(GET_OPCODE(*previous)) { | 96 | switch(GET_OPCODE(*previous)) { |
95 | case CONCOP: *previous = SETARG_U(*previous, GETARG_U(*previous)+1); break; | 97 | case CONCOP: SETARG_U(*previous, GETARG_U(*previous)+1); break; |
96 | default: luaK_primitivecode(ls, CREATE_U(CONCOP, 2)); | 98 | default: luaK_primitivecode(ls, CREATE_U(CONCOP, 2)); |
97 | } | 99 | } |
98 | } | 100 | } |
@@ -102,8 +104,8 @@ void luaK_retcode (LexState *ls, int nlocals, int nexps) { | |||
102 | Instruction *previous = previous_instruction(ls); | 104 | Instruction *previous = previous_instruction(ls); |
103 | if (nexps > 0 && GET_OPCODE(*previous) == CALL) { | 105 | if (nexps > 0 && GET_OPCODE(*previous) == CALL) { |
104 | LUA_ASSERT(ls->L, GETARG_B(*previous) == MULT_RET, "call should be open"); | 106 | LUA_ASSERT(ls->L, GETARG_B(*previous) == MULT_RET, "call should be open"); |
105 | *previous = SET_OPCODE(*previous, TAILCALL); | 107 | SET_OPCODE(*previous, TAILCALL); |
106 | *previous = SETARG_B(*previous, nlocals); | 108 | SETARG_B(*previous, nlocals); |
107 | } | 109 | } |
108 | else | 110 | else |
109 | luaK_primitivecode(ls, CREATE_U(RETCODE, nlocals)); | 111 | luaK_primitivecode(ls, CREATE_U(RETCODE, nlocals)); |
@@ -115,7 +117,7 @@ static void luaK_pushnil (LexState *ls, int n) { | |||
115 | luaK_deltastack(ls, n); | 117 | luaK_deltastack(ls, n); |
116 | switch(GET_OPCODE(*previous)) { | 118 | switch(GET_OPCODE(*previous)) { |
117 | case PUSHNIL: | 119 | case PUSHNIL: |
118 | *previous = SETARG_U(*previous, GETARG_U(*previous)+n); | 120 | SETARG_U(*previous, GETARG_U(*previous)+n); |
119 | break; | 121 | break; |
120 | default: luaK_primitivecode(ls, CREATE_U(PUSHNIL, n)); | 122 | default: luaK_primitivecode(ls, CREATE_U(PUSHNIL, n)); |
121 | } | 123 | } |
@@ -132,7 +134,7 @@ void luaK_fixjump (LexState *ls, int pc, int dest) { | |||
132 | FuncState *fs = ls->fs; | 134 | FuncState *fs = ls->fs; |
133 | Instruction *jmp = &fs->f->code[pc]; | 135 | Instruction *jmp = &fs->f->code[pc]; |
134 | /* jump is relative to position following jump instruction */ | 136 | /* jump is relative to position following jump instruction */ |
135 | *jmp = SETARG_S(*jmp, dest-(pc+1)); | 137 | SETARG_S(*jmp, dest-(pc+1)); |
136 | } | 138 | } |
137 | 139 | ||
138 | 140 | ||
@@ -213,7 +215,7 @@ void luaK_setcallreturns (LexState *ls, int nresults) { | |||
213 | Instruction *i = previous_instruction(ls); | 215 | Instruction *i = previous_instruction(ls); |
214 | if (GET_OPCODE(*i) == CALL) { /* expression is a function call? */ | 216 | if (GET_OPCODE(*i) == CALL) { /* expression is a function call? */ |
215 | LUA_ASSERT(ls->L, GETARG_B(*i) == MULT_RET, "call should be open"); | 217 | LUA_ASSERT(ls->L, GETARG_B(*i) == MULT_RET, "call should be open"); |
216 | *i = SETARG_B(*i, nresults); /* set nresults */ | 218 | SETARG_B(*i, nresults); /* set nresults */ |
217 | luaK_deltastack(ls, nresults); /* push results */ | 219 | luaK_deltastack(ls, nresults); /* push results */ |
218 | } | 220 | } |
219 | } | 221 | } |
@@ -224,41 +226,43 @@ static void assertglobal (LexState *ls, int index) { | |||
224 | } | 226 | } |
225 | 227 | ||
226 | 228 | ||
227 | void luaK_tostack (LexState *ls, expdesc *var) { | 229 | static int discharge (LexState *ls, expdesc *var) { |
228 | switch (var->k) { | 230 | switch (var->k) { |
229 | case VLOCAL: | 231 | case VLOCAL: |
230 | luaK_U(ls, PUSHLOCAL, var->info, 1); | 232 | luaK_U(ls, PUSHLOCAL, var->u.index, 1); |
231 | break; | 233 | break; |
232 | case VGLOBAL: | 234 | case VGLOBAL: |
233 | luaK_U(ls, GETGLOBAL, var->info, 1); | 235 | luaK_U(ls, GETGLOBAL, var->u.index, 1); |
234 | assertglobal(ls, var->info); /* make sure that there is a global */ | 236 | assertglobal(ls, var->u.index); /* make sure that there is a global */ |
235 | break; | 237 | break; |
236 | case VINDEXED: | 238 | case VINDEXED: |
237 | luaK_gettable(ls); | 239 | luaK_gettable(ls); |
238 | break; | 240 | break; |
239 | case VEXP: | 241 | case VEXP: |
240 | return; /* exp result is already on stack */ | 242 | return 0; /* nothing to do */ |
241 | } | 243 | } |
242 | var->k = VEXP; | 244 | var->k = VEXP; |
245 | var->u.l.t = var->u.l.f = 0; | ||
246 | return 1; | ||
243 | } | 247 | } |
244 | 248 | ||
245 | 249 | ||
246 | void luaK_1tostack (LexState *ls, expdesc *var) { | 250 | static void discharge1 (LexState *ls, expdesc *var) { |
247 | if (var->k == VEXP) | 251 | discharge(ls, var); |
252 | /* if it has jumps it is already discharged */ | ||
253 | if (var->u.l.t == 0 && var->u.l.f == 0) | ||
248 | luaK_setcallreturns(ls, 1); /* call must return 1 value */ | 254 | luaK_setcallreturns(ls, 1); /* call must return 1 value */ |
249 | else | ||
250 | luaK_tostack(ls, var); | ||
251 | } | 255 | } |
252 | 256 | ||
253 | 257 | ||
254 | void luaK_storevar (LexState *ls, const expdesc *var) { | 258 | void luaK_storevar (LexState *ls, const expdesc *var) { |
255 | switch (var->k) { | 259 | switch (var->k) { |
256 | case VLOCAL: | 260 | case VLOCAL: |
257 | luaK_U(ls, SETLOCAL, var->info, -1); | 261 | luaK_U(ls, SETLOCAL, var->u.index, -1); |
258 | break; | 262 | break; |
259 | case VGLOBAL: | 263 | case VGLOBAL: |
260 | luaK_U(ls, SETGLOBAL, var->info, -1); | 264 | luaK_U(ls, SETGLOBAL, var->u.index, -1); |
261 | assertglobal(ls, var->info); /* make sure that there is a global */ | 265 | assertglobal(ls, var->u.index); /* make sure that there is a global */ |
262 | break; | 266 | break; |
263 | case VINDEXED: | 267 | case VINDEXED: |
264 | luaK_0(ls, SETTABLEPOP, -3); | 268 | luaK_0(ls, SETTABLEPOP, -3); |
@@ -269,40 +273,228 @@ void luaK_storevar (LexState *ls, const expdesc *var) { | |||
269 | } | 273 | } |
270 | 274 | ||
271 | 275 | ||
276 | static OpCode invertjump (OpCode op) { | ||
277 | switch (op) { | ||
278 | case IFNEQJMP: return IFEQJMP; | ||
279 | case IFEQJMP: return IFNEQJMP; | ||
280 | case IFLTJMP: return IFGEJMP; | ||
281 | case IFLEJMP: return IFGTJMP; | ||
282 | case IFGTJMP: return IFLEJMP; | ||
283 | case IFGEJMP: return IFLTJMP; | ||
284 | default: | ||
285 | LUA_INTERNALERROR(NULL, "invalid jump instruction"); | ||
286 | return ENDCODE; /* to avoid warnings */ | ||
287 | } | ||
288 | } | ||
289 | |||
290 | |||
291 | static void insert_last (FuncState *fs, int *list) { | ||
292 | int temp = *list; | ||
293 | *list = fs->pc-1; | ||
294 | if (temp == 0) /* chain list */ | ||
295 | SETARG_S(fs->f->code[*list], 0); | ||
296 | else | ||
297 | SETARG_S(fs->f->code[*list], temp-fs->pc); | ||
298 | } | ||
299 | |||
300 | |||
301 | static void luaK_patchlistaux (LexState *ls, int list, int target, | ||
302 | OpCode special, int special_target) { | ||
303 | if (list != 0) { | ||
304 | Instruction *code = ls->fs->f->code; | ||
305 | for (;;) { | ||
306 | Instruction *i = &code[list]; | ||
307 | OpCode op = GET_OPCODE(*i); | ||
308 | int temp = GETARG_S(*i); | ||
309 | if (op == special) | ||
310 | SETARG_S(*i, special_target-(list+1)); | ||
311 | else { | ||
312 | SETARG_S(*i, target-(list+1)); | ||
313 | if (op == ONTJMP) | ||
314 | SET_OPCODE(*i, IFTJMP); | ||
315 | else if (op == ONFJMP) | ||
316 | SET_OPCODE(*i, IFFJMP); | ||
317 | } | ||
318 | if (temp == 0) return; | ||
319 | list += temp+1; | ||
320 | } | ||
321 | } | ||
322 | } | ||
323 | |||
324 | |||
325 | void luaK_patchlist (LexState *ls, int list, int target) { | ||
326 | luaK_patchlistaux(ls, list, target, ENDCODE, 0); | ||
327 | } | ||
328 | |||
329 | |||
330 | static int has_jumps (FuncState *fs, int list, OpCode ignore) { | ||
331 | if (list == 0) return 0; | ||
332 | else { | ||
333 | Instruction *code = fs->f->code; | ||
334 | for (;;) { | ||
335 | int temp = GETARG_S(code[list]); | ||
336 | if (GET_OPCODE(code[list]) != ignore) return 1; | ||
337 | else if (temp == 0) return 0; | ||
338 | list += temp+1; | ||
339 | } | ||
340 | } | ||
341 | } | ||
342 | |||
343 | |||
344 | static void concatlists (LexState *ls, int *l1, int l2) { | ||
345 | if (*l1 == 0) | ||
346 | *l1 = l2; | ||
347 | else if (l2 != 0) { | ||
348 | FuncState *fs = ls->fs; | ||
349 | int list = *l1; | ||
350 | for (;;) { /* traverse `l1' */ | ||
351 | int temp = GETARG_S(fs->f->code[list]); | ||
352 | if (temp == 0) { /* end of list? */ | ||
353 | SETARG_S(fs->f->code[list], l2-(list+1)); /* end points to `l2' */ | ||
354 | return; | ||
355 | } | ||
356 | list += temp+1; | ||
357 | } | ||
358 | } | ||
359 | } | ||
360 | |||
361 | |||
362 | void luaK_goiftrue (LexState *ls, expdesc *v, int keepvalue) { | ||
363 | FuncState *fs = ls->fs; | ||
364 | Instruction *previous; | ||
365 | discharge1(ls, v); | ||
366 | previous = &fs->f->code[fs->pc-1]; | ||
367 | if (ISJUMP(GET_OPCODE(*previous))) | ||
368 | SET_OPCODE(*previous, invertjump(GET_OPCODE(*previous))); | ||
369 | else { | ||
370 | OpCode jump = keepvalue ? ONFJMP : IFFJMP; | ||
371 | luaK_S(ls, jump, 0, -1); | ||
372 | } | ||
373 | insert_last(fs, &v->u.l.f); | ||
374 | luaK_patchlist(ls, v->u.l.t, luaK_getlabel(ls)); | ||
375 | v->u.l.t = 0; | ||
376 | } | ||
377 | |||
378 | |||
379 | void luaK_goiffalse (LexState *ls, expdesc *v, int keepvalue) { | ||
380 | FuncState *fs = ls->fs; | ||
381 | Instruction *previous; | ||
382 | discharge1(ls, v); | ||
383 | previous = &fs->f->code[fs->pc-1]; | ||
384 | if (!ISJUMP(GET_OPCODE(*previous))) { | ||
385 | OpCode jump = keepvalue ? ONTJMP : IFTJMP; | ||
386 | luaK_S(ls, jump, 0, -1); | ||
387 | } | ||
388 | insert_last(fs, &v->u.l.t); | ||
389 | luaK_patchlist(ls, v->u.l.f, luaK_getlabel(ls)); | ||
390 | v->u.l.f = 0; | ||
391 | } | ||
392 | |||
393 | |||
394 | void luaK_tostack (LexState *ls, expdesc *v, int onlyone) { | ||
395 | if (discharge(ls, v)) return; | ||
396 | else { /* is an expression */ | ||
397 | FuncState *fs = ls->fs; | ||
398 | Instruction *previous = &fs->f->code[fs->pc-1]; | ||
399 | if (!ISJUMP(GET_OPCODE(*previous)) && v->u.l.f == 0 && v->u.l.t == 0) { | ||
400 | /* it is an expression without jumps */ | ||
401 | if (onlyone && v->k == VEXP) | ||
402 | luaK_setcallreturns(ls, 1); /* call must return 1 value */ | ||
403 | return; | ||
404 | } | ||
405 | else { /* expression has jumps... */ | ||
406 | int p_nil = 0; /* position of an eventual PUSHNIL */ | ||
407 | int p_1 = 0; /* position of an eventual PUSHINT */ | ||
408 | int final; /* position after whole expression */ | ||
409 | if (ISJUMP(GET_OPCODE(*previous))) { | ||
410 | insert_last(fs, &v->u.l.t); | ||
411 | p_nil = luaK_0(ls, PUSHNILJMP, 0); | ||
412 | p_1 = luaK_S(ls, PUSHINT, 1, 1); | ||
413 | } | ||
414 | else { /* still may need a PUSHNIL or a PUSHINT */ | ||
415 | int need_nil = has_jumps(fs, v->u.l.f, ONFJMP); /* needs a PUSHNIL? */ | ||
416 | int need_1 = has_jumps(fs, v->u.l.t, ONTJMP); /* needs a PUSHINT? */ | ||
417 | if (need_nil && need_1) { | ||
418 | luaK_S(ls, JMP, 2, 0); | ||
419 | p_nil = luaK_0(ls, PUSHNILJMP, 0); | ||
420 | p_1 = luaK_S(ls, PUSHINT, 1, 1); | ||
421 | } | ||
422 | else if (need_nil || need_1) { | ||
423 | luaK_S(ls, JMP, 1, 0); | ||
424 | if (need_nil) | ||
425 | p_nil = luaK_0(ls, PUSHNIL, 1); | ||
426 | else /* need_1 */ | ||
427 | p_1 = luaK_S(ls, PUSHINT, 1, 1); | ||
428 | } | ||
429 | } | ||
430 | final = luaK_getlabel(ls); | ||
431 | luaK_patchlistaux(ls, v->u.l.f, p_nil, ONFJMP, final); | ||
432 | luaK_patchlistaux(ls, v->u.l.t, p_1, ONTJMP, final); | ||
433 | v->u.l.f = v->u.l.t = 0; | ||
434 | } | ||
435 | } | ||
436 | } | ||
437 | |||
438 | |||
272 | void luaK_prefix (LexState *ls, int op, expdesc *v) { | 439 | void luaK_prefix (LexState *ls, int op, expdesc *v) { |
273 | luaK_1tostack(ls, v); | 440 | if (op == '-') { |
274 | if (op == '-') luaK_minus(ls); | 441 | luaK_tostack(ls, v, 1); |
275 | else luaK_0(ls, NOTOP, 0); | 442 | luaK_minus(ls); |
443 | } | ||
444 | else { /* op == NOT */ | ||
445 | FuncState *fs = ls->fs; | ||
446 | Instruction *previous; | ||
447 | discharge1(ls, v); | ||
448 | previous = &fs->f->code[fs->pc-1]; | ||
449 | if (ISJUMP(GET_OPCODE(*previous))) | ||
450 | SET_OPCODE(*previous, invertjump(GET_OPCODE(*previous))); | ||
451 | else | ||
452 | luaK_0(ls, NOTOP, 0); | ||
453 | /* interchange true and false lists */ | ||
454 | { int temp = v->u.l.f; v->u.l.f = v->u.l.t; v->u.l.t = temp; } | ||
455 | } | ||
276 | } | 456 | } |
277 | 457 | ||
278 | 458 | ||
279 | void luaK_infix (LexState *ls, int op, expdesc *v) { | 459 | void luaK_infix (LexState *ls, int op, expdesc *v) { |
280 | luaK_1tostack(ls, v); | ||
281 | if (op == AND) | 460 | if (op == AND) |
282 | v->info = luaK_0(ls, ONFJMP, -1); | 461 | luaK_goiftrue(ls, v, 1); |
283 | else if (op == OR) | 462 | else if (op == OR) |
284 | v->info = luaK_0(ls, ONTJMP, -1); | 463 | luaK_goiffalse(ls, v, 1); |
464 | else | ||
465 | luaK_tostack(ls, v, 1); | ||
285 | } | 466 | } |
286 | 467 | ||
287 | 468 | ||
288 | void luaK_posfix (LexState *ls, int op, expdesc *v1, expdesc *v2) { | 469 | void luaK_posfix (LexState *ls, int op, expdesc *v1, expdesc *v2) { |
289 | luaK_1tostack(ls, v2); | 470 | if (op == AND) { |
290 | switch (op) { | 471 | LUA_ASSERT(ls->L, v1->u.l.t == 0, "list must be closed"); |
291 | case AND: case OR: | 472 | discharge1(ls, v2); |
292 | luaK_fixjump(ls, v1->info, luaK_getlabel(ls)); | 473 | v1->u.l.t = v2->u.l.t; |
293 | break; | 474 | concatlists(ls, &v1->u.l.f, v2->u.l.f); |
294 | case '+': luaK_add(ls); break; | 475 | } |
295 | case '-': luaK_sub(ls); break; | 476 | else if (op == OR) { |
296 | case '*': luaK_0(ls, MULTOP, -1); break; | 477 | LUA_ASSERT(ls->L, v1->u.l.f == 0, "list must be closed"); |
297 | case '/': luaK_0(ls, DIVOP, -1); break; | 478 | discharge1(ls, v2); |
298 | case '^': luaK_0(ls, POWOP, -1); break; | 479 | v1->u.l.f = v2->u.l.f; |
299 | case CONC: luaK_conc(ls); break; | 480 | concatlists(ls, &v1->u.l.t, v2->u.l.t); |
300 | case EQ: luaK_0(ls, EQOP, -1); break; | 481 | } |
301 | case NE: luaK_0(ls, NEQOP, -1); break; | 482 | else { |
302 | case '>': luaK_0(ls, GTOP, -1); break; | 483 | luaK_tostack(ls, v2, 1); /* `v2' must have a value */ |
303 | case '<': luaK_0(ls, LTOP, -1); break; | 484 | switch (op) { |
304 | case GE: luaK_0(ls, GEOP, -1); break; | 485 | case '+': luaK_add(ls); break; |
305 | case LE: luaK_0(ls, LEOP, -1); break; | 486 | case '-': luaK_sub(ls); break; |
487 | case '*': luaK_0(ls, MULTOP, -1); break; | ||
488 | case '/': luaK_0(ls, DIVOP, -1); break; | ||
489 | case '^': luaK_0(ls, POWOP, -1); break; | ||
490 | case CONC: luaK_conc(ls); break; | ||
491 | case EQ: luaK_S(ls, IFEQJMP, 0, -2); break; | ||
492 | case NE: luaK_S(ls, IFNEQJMP, 0, -2); break; | ||
493 | case '>': luaK_S(ls, IFGTJMP, 0, -2); break; | ||
494 | case '<': luaK_S(ls, IFLTJMP, 0, -2); break; | ||
495 | case GE: luaK_S(ls, IFGEJMP, 0, -2); break; | ||
496 | case LE: luaK_S(ls, IFLEJMP, 0, -2); break; | ||
497 | } | ||
306 | } | 498 | } |
307 | } | 499 | } |
308 | 500 | ||