| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library dev_compiler.src.codegen.ast_builder; | 5 library dev_compiler.src.codegen.ast_builder; |
| 6 | 6 |
| 7 import 'package:analyzer/src/generated/ast.dart'; | 7 import 'package:analyzer/src/generated/ast.dart'; |
| 8 import 'package:analyzer/src/generated/scanner.dart'; | 8 import 'package:analyzer/src/generated/scanner.dart'; |
| 9 import 'package:analyzer/src/generated/utilities_dart.dart'; | 9 import 'package:analyzer/src/generated/utilities_dart.dart'; |
| 10 import 'package:logging/logging.dart' as logger; | 10 import 'package:logging/logging.dart' as logger; |
| 11 | 11 |
| 12 final _log = new logger.Logger('dev_compiler.ast_builder'); | 12 final _log = new logger.Logger('dev_compiler.ast_builder'); |
| 13 | 13 |
| 14 // Wrappers around constructors for the dart ast. The AstBuilder class | 14 // Wrappers around constructors for the dart ast. The AstBuilder class |
| 15 // provides a higher-level interface, abstracting both from the lexical | 15 // provides a higher-level interface, abstracting both from the lexical |
| 16 // details and some of helper classes. The RawAstBuilder class provides | 16 // details and some of helper classes. The RawAstBuilder class provides |
| 17 // a low-level wrapper class (below) abstracts from the lexical details | 17 // a low-level wrapper class (below) abstracts from the lexical details |
| 18 // but otherwise faithfully mirrors the construction API. | 18 // but otherwise faithfully mirrors the construction API. |
| 19 class AstBuilder { | 19 class AstBuilder { |
| 20 static SimpleIdentifier identifierFromString(String name) { | 20 static SimpleIdentifier identifierFromString(String name) { |
| 21 return RawAstBuilder.identifierFromString(name); | 21 return RawAstBuilder.identifierFromString(name); |
| 22 } | 22 } |
| 23 | 23 |
| 24 static PrefixedIdentifier prefixedIdentifier(Identifier pre, Identifier id) { | 24 static PrefixedIdentifier prefixedIdentifier( |
| 25 SimpleIdentifier pre, SimpleIdentifier id) { |
| 25 return RawAstBuilder.prefixedIdentifier(pre, id); | 26 return RawAstBuilder.prefixedIdentifier(pre, id); |
| 26 } | 27 } |
| 27 | 28 |
| 28 static TypeParameter typeParameter(Identifier name, [TypeName bound = null]) { | 29 static TypeParameter typeParameter(SimpleIdentifier name, |
| 30 [TypeName bound = null]) { |
| 29 return RawAstBuilder.typeParameter(name, bound); | 31 return RawAstBuilder.typeParameter(name, bound); |
| 30 } | 32 } |
| 31 | 33 |
| 32 static TypeParameterList typeParameterList(List<TypeParameter> params) { | 34 static TypeParameterList typeParameterList(List<TypeParameter> params) { |
| 33 return RawAstBuilder.typeParameterList(params); | 35 return RawAstBuilder.typeParameterList(params); |
| 34 } | 36 } |
| 35 | 37 |
| 36 static TypeArgumentList typeArgumentList(List<TypeName> args) { | 38 static TypeArgumentList typeArgumentList(List<TypeName> args) { |
| 37 return RawAstBuilder.typeArgumentList(args); | 39 return RawAstBuilder.typeArgumentList(args); |
| 38 } | 40 } |
| 39 | 41 |
| 40 static ArgumentList argumentList(List<Expression> args) { | 42 static ArgumentList argumentList(List<Expression> args) { |
| 41 return RawAstBuilder.argumentList(args); | 43 return RawAstBuilder.argumentList(args); |
| 42 } | 44 } |
| 43 | 45 |
| 44 static TypeName typeName(Identifier id, List<TypeName> args) { | 46 static TypeName typeName(Identifier id, List<TypeName> args) { |
| 45 TypeArgumentList argList = null; | 47 TypeArgumentList argList = null; |
| 46 if (args != null && args.length > 0) argList = typeArgumentList(args); | 48 if (args != null && args.length > 0) argList = typeArgumentList(args); |
| 47 return RawAstBuilder.typeName(id, argList); | 49 return RawAstBuilder.typeName(id, argList); |
| 48 } | 50 } |
| 49 | 51 |
| 50 static FunctionTypeAlias functionTypeAlias(TypeName ret, Identifier name, | 52 static FunctionTypeAlias functionTypeAlias(TypeName ret, |
| 51 List<TypeParameter> tParams, List<FormalParameter> params) { | 53 SimpleIdentifier name, List<TypeParameter> tParams, |
| 54 List<FormalParameter> params) { |
| 52 TypeParameterList tps = | 55 TypeParameterList tps = |
| 53 (tParams.length == 0) ? null : typeParameterList(tParams); | 56 (tParams.length == 0) ? null : typeParameterList(tParams); |
| 54 FormalParameterList fps = formalParameterList(params); | 57 FormalParameterList fps = formalParameterList(params); |
| 55 return RawAstBuilder.functionTypeAlias(ret, name, tps, fps); | 58 return RawAstBuilder.functionTypeAlias(ret, name, tps, fps); |
| 56 } | 59 } |
| 57 | 60 |
| 58 static BooleanLiteral booleanLiteral(bool b) { | 61 static BooleanLiteral booleanLiteral(bool b) { |
| 59 return RawAstBuilder.booleanLiteral(b); | 62 return RawAstBuilder.booleanLiteral(b); |
| 60 } | 63 } |
| 61 | 64 |
| (...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 241 } | 244 } |
| 242 | 245 |
| 243 static FormalParameterList formalParameterList(List<FormalParameter> params) { | 246 static FormalParameterList formalParameterList(List<FormalParameter> params) { |
| 244 return RawAstBuilder.formalParameterList(params); | 247 return RawAstBuilder.formalParameterList(params); |
| 245 } | 248 } |
| 246 | 249 |
| 247 static Block block(List<Statement> statements) { | 250 static Block block(List<Statement> statements) { |
| 248 return RawAstBuilder.block(statements); | 251 return RawAstBuilder.block(statements); |
| 249 } | 252 } |
| 250 | 253 |
| 251 static MethodDeclaration blockMethodDeclaration(TypeName rt, Identifier m, | 254 static MethodDeclaration blockMethodDeclaration(TypeName rt, |
| 252 List<FormalParameter> params, List<Statement> statements, | 255 SimpleIdentifier m, List<FormalParameter> params, |
| 253 {bool isStatic: false}) { | 256 List<Statement> statements, {bool isStatic: false}) { |
| 254 FormalParameterList fl = formalParameterList(params); | 257 FormalParameterList fl = formalParameterList(params); |
| 255 Block b = block(statements); | 258 Block b = block(statements); |
| 256 BlockFunctionBody body = RawAstBuilder.blockFunctionBody(b); | 259 BlockFunctionBody body = RawAstBuilder.blockFunctionBody(b); |
| 257 return RawAstBuilder.methodDeclaration(rt, m, fl, body, isStatic: isStatic); | 260 return RawAstBuilder.methodDeclaration(rt, m, fl, body, isStatic: isStatic); |
| 258 } | 261 } |
| 259 | 262 |
| 260 static FunctionDeclaration blockFunctionDeclaration(TypeName rt, Identifier f, | 263 static FunctionDeclaration blockFunctionDeclaration(TypeName rt, |
| 261 List<FormalParameter> params, List<Statement> statements) { | 264 SimpleIdentifier f, List<FormalParameter> params, |
| 265 List<Statement> statements) { |
| 262 FunctionExpression fexp = blockFunction(params, statements); | 266 FunctionExpression fexp = blockFunction(params, statements); |
| 263 return RawAstBuilder.functionDeclaration(rt, f, fexp); | 267 return RawAstBuilder.functionDeclaration(rt, f, fexp); |
| 264 } | 268 } |
| 265 | 269 |
| 266 static FunctionExpression blockFunction( | 270 static FunctionExpression blockFunction( |
| 267 List<FormalParameter> params, List<Statement> statements) { | 271 List<FormalParameter> params, List<Statement> statements) { |
| 268 FormalParameterList fl = formalParameterList(params); | 272 FormalParameterList fl = formalParameterList(params); |
| 269 Block b = block(statements); | 273 Block b = block(statements); |
| 270 BlockFunctionBody body = RawAstBuilder.blockFunctionBody(b); | 274 BlockFunctionBody body = RawAstBuilder.blockFunctionBody(b); |
| 271 return RawAstBuilder.functionExpression(fl, body); | 275 return RawAstBuilder.functionExpression(fl, body); |
| 272 } | 276 } |
| 273 | 277 |
| 274 static FunctionExpression expressionFunction( | 278 static FunctionExpression expressionFunction( |
| 275 List<FormalParameter> params, Expression body, [bool decl = false]) { | 279 List<FormalParameter> params, Expression body, [bool decl = false]) { |
| 276 FormalParameterList fl = formalParameterList(params); | 280 FormalParameterList fl = formalParameterList(params); |
| 277 ExpressionFunctionBody b = RawAstBuilder.expressionFunctionBody(body, decl); | 281 ExpressionFunctionBody b = RawAstBuilder.expressionFunctionBody(body, decl); |
| 278 return RawAstBuilder.functionExpression(fl, b); | 282 return RawAstBuilder.functionExpression(fl, b); |
| 279 } | 283 } |
| 280 | 284 |
| 281 static FunctionDeclarationStatement functionDeclarationStatement( | 285 static FunctionDeclarationStatement functionDeclarationStatement( |
| 282 TypeName rType, Identifier name, FunctionExpression fe) { | 286 TypeName rType, SimpleIdentifier name, FunctionExpression fe) { |
| 283 var fd = RawAstBuilder.functionDeclaration(rType, name, fe); | 287 var fd = RawAstBuilder.functionDeclaration(rType, name, fe); |
| 284 return RawAstBuilder.functionDeclarationStatement(fd); | 288 return RawAstBuilder.functionDeclarationStatement(fd); |
| 285 } | 289 } |
| 286 | 290 |
| 287 static Statement returnExpression([Expression e]) { | 291 static Statement returnExpression([Expression e]) { |
| 288 return RawAstBuilder.returnExpression(e); | 292 return RawAstBuilder.returnExpression(e); |
| 289 } | 293 } |
| 290 | 294 |
| 291 // let b = e1 in e2 == (\b.e2)(e1) | 295 // let b = e1 in e2 == (\b.e2)(e1) |
| 292 static Expression letExpression( | 296 static Expression letExpression( |
| 293 FormalParameter b, Expression e1, Expression e2) { | 297 FormalParameter b, Expression e1, Expression e2) { |
| 294 FunctionExpression l = expressionFunction(<FormalParameter>[b], e2); | 298 FunctionExpression l = expressionFunction(<FormalParameter>[b], e2); |
| 295 return application(parenthesize(l), <Expression>[e1]); | 299 return application(parenthesize(l), <Expression>[e1]); |
| 296 } | 300 } |
| 297 | 301 |
| 298 static SimpleFormalParameter simpleFormal(Identifier v, TypeName t) { | 302 static SimpleFormalParameter simpleFormal(SimpleIdentifier v, TypeName t) { |
| 299 return RawAstBuilder.simpleFormalParameter(v, t); | 303 return RawAstBuilder.simpleFormalParameter(v, t); |
| 300 } | 304 } |
| 301 | 305 |
| 302 static FunctionTypedFormalParameter functionTypedFormal( | 306 static FunctionTypedFormalParameter functionTypedFormal( |
| 303 TypeName ret, Identifier v, List<FormalParameter> params) { | 307 TypeName ret, SimpleIdentifier v, List<FormalParameter> params) { |
| 304 FormalParameterList ps = formalParameterList(params); | 308 FormalParameterList ps = formalParameterList(params); |
| 305 return RawAstBuilder.functionTypedFormalParameter(ret, v, ps); | 309 return RawAstBuilder.functionTypedFormalParameter(ret, v, ps); |
| 306 } | 310 } |
| 307 | 311 |
| 308 static FormalParameter requiredFormal(NormalFormalParameter fp) { | 312 static FormalParameter requiredFormal(NormalFormalParameter fp) { |
| 309 return RawAstBuilder.requiredFormalParameter(fp); | 313 return RawAstBuilder.requiredFormalParameter(fp); |
| 310 } | 314 } |
| 311 | 315 |
| 312 static FormalParameter optionalFormal(NormalFormalParameter fp) { | 316 static FormalParameter optionalFormal(NormalFormalParameter fp) { |
| 313 return RawAstBuilder.optionalFormalParameter(fp); | 317 return RawAstBuilder.optionalFormalParameter(fp); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 327 } | 331 } |
| 328 | 332 |
| 329 // This class provides a low-level wrapper around the constructors for | 333 // This class provides a low-level wrapper around the constructors for |
| 330 // the AST. It mostly simply abstracts from the lexical tokens. | 334 // the AST. It mostly simply abstracts from the lexical tokens. |
| 331 class RawAstBuilder { | 335 class RawAstBuilder { |
| 332 static SimpleIdentifier identifierFromString(String name) { | 336 static SimpleIdentifier identifierFromString(String name) { |
| 333 StringToken token = new SyntheticStringToken(TokenType.IDENTIFIER, name, 0); | 337 StringToken token = new SyntheticStringToken(TokenType.IDENTIFIER, name, 0); |
| 334 return new SimpleIdentifier(token); | 338 return new SimpleIdentifier(token); |
| 335 } | 339 } |
| 336 | 340 |
| 337 static PrefixedIdentifier prefixedIdentifier(Identifier pre, Identifier id) { | 341 static PrefixedIdentifier prefixedIdentifier( |
| 342 SimpleIdentifier pre, SimpleIdentifier id) { |
| 338 Token period = new Token(TokenType.PERIOD, 0); | 343 Token period = new Token(TokenType.PERIOD, 0); |
| 339 return new PrefixedIdentifier(pre, period, id); | 344 return new PrefixedIdentifier(pre, period, id); |
| 340 } | 345 } |
| 341 | 346 |
| 342 static TypeParameter typeParameter(Identifier name, [TypeName bound = null]) { | 347 static TypeParameter typeParameter(SimpleIdentifier name, |
| 348 [TypeName bound = null]) { |
| 343 Token keyword = | 349 Token keyword = |
| 344 (bound == null) ? null : new KeywordToken(Keyword.EXTENDS, 0); | 350 (bound == null) ? null : new KeywordToken(Keyword.EXTENDS, 0); |
| 345 return new TypeParameter(null, null, name, keyword, bound); | 351 return new TypeParameter(null, null, name, keyword, bound); |
| 346 } | 352 } |
| 347 | 353 |
| 348 static TypeParameterList typeParameterList(List<TypeParameter> params) { | 354 static TypeParameterList typeParameterList(List<TypeParameter> params) { |
| 349 Token lb = new Token(TokenType.LT, 0); | 355 Token lb = new Token(TokenType.LT, 0); |
| 350 Token rb = new Token(TokenType.GT, 0); | 356 Token rb = new Token(TokenType.GT, 0); |
| 351 return new TypeParameterList(lb, params, rb); | 357 return new TypeParameterList(lb, params, rb); |
| 352 } | 358 } |
| 353 | 359 |
| 354 static TypeArgumentList typeArgumentList(List<TypeName> args) { | 360 static TypeArgumentList typeArgumentList(List<TypeName> args) { |
| 355 Token lb = new Token(TokenType.LT, 0); | 361 Token lb = new Token(TokenType.LT, 0); |
| 356 Token rb = new Token(TokenType.GT, 0); | 362 Token rb = new Token(TokenType.GT, 0); |
| 357 return new TypeArgumentList(lb, args, rb); | 363 return new TypeArgumentList(lb, args, rb); |
| 358 } | 364 } |
| 359 | 365 |
| 360 static ArgumentList argumentList(List<Expression> args) { | 366 static ArgumentList argumentList(List<Expression> args) { |
| 361 Token lp = new BeginToken(TokenType.OPEN_PAREN, 0); | 367 Token lp = new BeginToken(TokenType.OPEN_PAREN, 0); |
| 362 Token rp = new Token(TokenType.CLOSE_PAREN, 0); | 368 Token rp = new Token(TokenType.CLOSE_PAREN, 0); |
| 363 return new ArgumentList(lp, args, rp); | 369 return new ArgumentList(lp, args, rp); |
| 364 } | 370 } |
| 365 | 371 |
| 366 static TypeName typeName(Identifier id, TypeArgumentList l) { | 372 static TypeName typeName(Identifier id, TypeArgumentList l) { |
| 367 return new TypeName(id, l); | 373 return new TypeName(id, l); |
| 368 } | 374 } |
| 369 | 375 |
| 370 static FunctionTypeAlias functionTypeAlias(TypeName ret, Identifier name, | 376 static FunctionTypeAlias functionTypeAlias(TypeName ret, |
| 371 TypeParameterList tps, FormalParameterList fps) { | 377 SimpleIdentifier name, TypeParameterList tps, FormalParameterList fps) { |
| 372 Token semi = new Token(TokenType.SEMICOLON, 0); | 378 Token semi = new Token(TokenType.SEMICOLON, 0); |
| 373 Token td = new KeywordToken(Keyword.TYPEDEF, 0); | 379 Token td = new KeywordToken(Keyword.TYPEDEF, 0); |
| 374 return new FunctionTypeAlias(null, null, td, ret, name, tps, fps, semi); | 380 return new FunctionTypeAlias(null, null, td, ret, name, tps, fps, semi); |
| 375 } | 381 } |
| 376 | 382 |
| 377 static BooleanLiteral booleanLiteral(bool b) { | 383 static BooleanLiteral booleanLiteral(bool b) { |
| 378 var k = new KeywordToken(b ? Keyword.TRUE : Keyword.FALSE, 0); | 384 var k = new KeywordToken(b ? Keyword.TRUE : Keyword.FALSE, 0); |
| 379 return new BooleanLiteral(k, b); | 385 return new BooleanLiteral(k, b); |
| 380 } | 386 } |
| 381 | 387 |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 461 return new BlockFunctionBody(null, null, b); | 467 return new BlockFunctionBody(null, null, b); |
| 462 } | 468 } |
| 463 | 469 |
| 464 static ExpressionFunctionBody expressionFunctionBody(Expression body, | 470 static ExpressionFunctionBody expressionFunctionBody(Expression body, |
| 465 [bool decl = false]) { | 471 [bool decl = false]) { |
| 466 Token semi = (decl) ? new Token(TokenType.SEMICOLON, 0) : null; | 472 Token semi = (decl) ? new Token(TokenType.SEMICOLON, 0) : null; |
| 467 return new ExpressionFunctionBody(null, null, body, semi); | 473 return new ExpressionFunctionBody(null, null, body, semi); |
| 468 } | 474 } |
| 469 | 475 |
| 470 static FunctionDeclaration functionDeclaration( | 476 static FunctionDeclaration functionDeclaration( |
| 471 TypeName rt, Identifier f, FunctionExpression fexp) { | 477 TypeName rt, SimpleIdentifier f, FunctionExpression fexp) { |
| 472 return new FunctionDeclaration(null, null, null, rt, null, f, fexp); | 478 return new FunctionDeclaration(null, null, null, rt, null, f, fexp); |
| 473 } | 479 } |
| 474 | 480 |
| 475 static MethodDeclaration methodDeclaration( | 481 static MethodDeclaration methodDeclaration(TypeName rt, SimpleIdentifier m, |
| 476 TypeName rt, Identifier m, FormalParameterList fl, FunctionBody body, | 482 FormalParameterList fl, FunctionBody body, {bool isStatic: false}) { |
| 477 {bool isStatic: false}) { | |
| 478 Token st = isStatic ? new KeywordToken(Keyword.STATIC, 0) : null; | 483 Token st = isStatic ? new KeywordToken(Keyword.STATIC, 0) : null; |
| 479 return new MethodDeclaration( | 484 return new MethodDeclaration( |
| 480 null, null, null, st, rt, null, null, m, null, fl, body); | 485 null, null, null, st, rt, null, null, m, null, fl, body); |
| 481 } | 486 } |
| 482 | 487 |
| 483 static FunctionExpression functionExpression( | 488 static FunctionExpression functionExpression( |
| 484 FormalParameterList fl, FunctionBody body) { | 489 FormalParameterList fl, FunctionBody body) { |
| 485 return new FunctionExpression(null, fl, body); | 490 return new FunctionExpression(null, fl, body); |
| 486 } | 491 } |
| 487 | 492 |
| 488 static FunctionDeclarationStatement functionDeclarationStatement( | 493 static FunctionDeclarationStatement functionDeclarationStatement( |
| 489 FunctionDeclaration fd) { | 494 FunctionDeclaration fd) { |
| 490 return new FunctionDeclarationStatement(fd); | 495 return new FunctionDeclarationStatement(fd); |
| 491 } | 496 } |
| 492 | 497 |
| 493 static Statement returnExpression([Expression e]) { | 498 static Statement returnExpression([Expression e]) { |
| 494 Token ret = new KeywordToken(Keyword.RETURN, 0); | 499 Token ret = new KeywordToken(Keyword.RETURN, 0); |
| 495 Token semi = new Token(TokenType.SEMICOLON, 0); | 500 Token semi = new Token(TokenType.SEMICOLON, 0); |
| 496 return new ReturnStatement(ret, e, semi); | 501 return new ReturnStatement(ret, e, semi); |
| 497 } | 502 } |
| 498 | 503 |
| 499 static SimpleFormalParameter simpleFormalParameter(Identifier v, TypeName t) { | 504 static SimpleFormalParameter simpleFormalParameter( |
| 505 SimpleIdentifier v, TypeName t) { |
| 500 return new SimpleFormalParameter(null, <Annotation>[], null, t, v); | 506 return new SimpleFormalParameter(null, <Annotation>[], null, t, v); |
| 501 } | 507 } |
| 502 | 508 |
| 503 static FunctionTypedFormalParameter functionTypedFormalParameter( | 509 static FunctionTypedFormalParameter functionTypedFormalParameter( |
| 504 TypeName ret, Identifier v, FormalParameterList ps) { | 510 TypeName ret, SimpleIdentifier v, FormalParameterList ps) { |
| 505 return new FunctionTypedFormalParameter( | 511 return new FunctionTypedFormalParameter( |
| 506 null, <Annotation>[], ret, v, null, ps); | 512 null, <Annotation>[], ret, v, null, ps); |
| 507 } | 513 } |
| 508 | 514 |
| 509 static FormalParameter requiredFormalParameter(NormalFormalParameter fp) { | 515 static FormalParameter requiredFormalParameter(NormalFormalParameter fp) { |
| 510 return fp; | 516 return fp; |
| 511 } | 517 } |
| 512 | 518 |
| 513 static FormalParameter optionalFormalParameter(NormalFormalParameter fp) { | 519 static FormalParameter optionalFormalParameter(NormalFormalParameter fp) { |
| 514 return new DefaultFormalParameter(fp, ParameterKind.POSITIONAL, null, null); | 520 return new DefaultFormalParameter(fp, ParameterKind.POSITIONAL, null, null); |
| 515 } | 521 } |
| 516 | 522 |
| 517 static FormalParameter namedFormalParameter(NormalFormalParameter fp) { | 523 static FormalParameter namedFormalParameter(NormalFormalParameter fp) { |
| 518 return new DefaultFormalParameter(fp, ParameterKind.NAMED, null, null); | 524 return new DefaultFormalParameter(fp, ParameterKind.NAMED, null, null); |
| 519 } | 525 } |
| 520 | 526 |
| 521 static NamedExpression namedParameter(Identifier s, Expression e) { | 527 static NamedExpression namedParameter(SimpleIdentifier s, Expression e) { |
| 522 return namedExpression(s, e); | 528 return namedExpression(s, e); |
| 523 } | 529 } |
| 524 | 530 |
| 525 static NamedExpression namedExpression(Identifier s, Expression e) { | 531 static NamedExpression namedExpression(SimpleIdentifier s, Expression e) { |
| 526 Label l = new Label(s, new Token(TokenType.COLON, 0)); | 532 Label l = new Label(s, new Token(TokenType.COLON, 0)); |
| 527 return new NamedExpression(l, e); | 533 return new NamedExpression(l, e); |
| 528 } | 534 } |
| 529 } | 535 } |
| OLD | NEW |