Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, 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 fasta.source_library_builder; | 5 library fasta.source_library_builder; |
| 6 | 6 |
| 7 import 'package:kernel/ast.dart' show AsyncMarker, ProcedureKind; | 7 import 'package:kernel/ast.dart' show AsyncMarker, ProcedureKind; |
| 8 | 8 |
| 9 import '../combinator.dart' show Combinator; | 9 import '../combinator.dart' show Combinator; |
| 10 | 10 |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 31 Scope, | 31 Scope, |
| 32 TypeBuilder, | 32 TypeBuilder, |
| 33 TypeDeclarationBuilder, | 33 TypeDeclarationBuilder, |
| 34 TypeVariableBuilder, | 34 TypeVariableBuilder, |
| 35 Unhandled; | 35 Unhandled; |
| 36 | 36 |
| 37 abstract class SourceLibraryBuilder<T extends TypeBuilder, R> | 37 abstract class SourceLibraryBuilder<T extends TypeBuilder, R> |
| 38 extends LibraryBuilder<T, R> { | 38 extends LibraryBuilder<T, R> { |
| 39 final SourceLoader loader; | 39 final SourceLoader loader; |
| 40 | 40 |
| 41 final DeclarationBuilder<T> libraryDeclaration = | 41 final DeclarationBuilder<T> libraryDeclaration; |
| 42 new DeclarationBuilder<T>(<String, Builder>{}, null); | |
| 43 | 42 |
| 44 final List<ConstructorReferenceBuilder> constructorReferences = | 43 final List<ConstructorReferenceBuilder> constructorReferences = |
| 45 <ConstructorReferenceBuilder>[]; | 44 <ConstructorReferenceBuilder>[]; |
| 46 | 45 |
| 47 final List<SourceLibraryBuilder<T, R>> parts = <SourceLibraryBuilder<T, R>>[]; | 46 final List<SourceLibraryBuilder<T, R>> parts = <SourceLibraryBuilder<T, R>>[]; |
| 48 | 47 |
| 49 final List<Import> imports = <Import>[]; | 48 final List<Import> imports = <Import>[]; |
| 50 | 49 |
| 51 final Map<String, Builder> exports = <String, Builder>{}; | 50 final Scope importScope; |
| 52 | |
| 53 final Scope scope = new Scope.top(); | |
| 54 | 51 |
| 55 final Uri fileUri; | 52 final Uri fileUri; |
| 56 | 53 |
| 57 final List<List> implementationBuilders = <List<List>>[]; | 54 final List<List> implementationBuilders = <List<List>>[]; |
| 58 | 55 |
| 59 String name; | 56 String name; |
| 60 | 57 |
| 61 String partOfName; | 58 String partOfName; |
| 62 | 59 |
| 63 Uri partOfUri; | 60 Uri partOfUri; |
| 64 | 61 |
| 65 List<MetadataBuilder> metadata; | 62 List<MetadataBuilder> metadata; |
| 66 | 63 |
| 67 /// The current declaration that is being built. When we start parsing a | 64 /// The current declaration that is being built. When we start parsing a |
| 68 /// declaration (class, method, and so on), we don't have enough information | 65 /// declaration (class, method, and so on), we don't have enough information |
| 69 /// to create a builder and this object records its members and types until, | 66 /// to create a builder and this object records its members and types until, |
| 70 /// for example, [addClass] is called. | 67 /// for example, [addClass] is called. |
| 71 DeclarationBuilder<T> currentDeclaration; | 68 DeclarationBuilder<T> currentDeclaration; |
| 72 | 69 |
| 73 bool canAddImplementationBuilders = false; | 70 bool canAddImplementationBuilders = false; |
| 74 | 71 |
| 75 SourceLibraryBuilder(this.loader, Uri fileUri) | 72 SourceLibraryBuilder(SourceLoader loader, Uri fileUri) |
| 76 : fileUri = fileUri, | 73 : this.fromScopes(loader, fileUri, new DeclarationBuilder<T>.library(), |
| 77 super(fileUri) { | 74 new Scope.top()); |
| 78 currentDeclaration = libraryDeclaration; | 75 |
| 79 } | 76 SourceLibraryBuilder.fromScopes( |
| 77 this.loader, this.fileUri, this.libraryDeclaration, this.importScope) | |
| 78 : currentDeclaration = libraryDeclaration, | |
| 79 super( | |
| 80 fileUri, libraryDeclaration.toScope(importScope), new Scope.top()); | |
| 80 | 81 |
| 81 Uri get uri; | 82 Uri get uri; |
| 82 | 83 |
| 83 bool get isPart => partOfName != null || partOfUri != null; | 84 bool get isPart => partOfName != null || partOfUri != null; |
| 84 | 85 |
| 85 Map<String, Builder> get members => libraryDeclaration.members; | |
| 86 | |
| 87 List<T> get types => libraryDeclaration.types; | 86 List<T> get types => libraryDeclaration.types; |
| 88 | 87 |
| 89 T addNamedType(String name, List<T> arguments, int charOffset); | 88 T addNamedType(String name, List<T> arguments, int charOffset); |
| 90 | 89 |
| 91 T addMixinApplication(T supertype, List<T> mixins, int charOffset); | 90 T addMixinApplication(T supertype, List<T> mixins, int charOffset); |
| 92 | 91 |
| 93 T addType(T type) { | 92 T addType(T type) { |
| 94 currentDeclaration.addType(type); | 93 currentDeclaration.addType(type); |
| 95 return type; | 94 return type; |
| 96 } | 95 } |
| 97 | 96 |
| 98 T addVoidType(int charOffset); | 97 T addVoidType(int charOffset); |
| 99 | 98 |
| 100 ConstructorReferenceBuilder addConstructorReference( | 99 ConstructorReferenceBuilder addConstructorReference( |
| 101 String name, List<T> typeArguments, String suffix, int charOffset) { | 100 String name, List<T> typeArguments, String suffix, int charOffset) { |
| 102 ConstructorReferenceBuilder ref = new ConstructorReferenceBuilder( | 101 ConstructorReferenceBuilder ref = new ConstructorReferenceBuilder( |
| 103 name, typeArguments, suffix, this, charOffset); | 102 name, typeArguments, suffix, this, charOffset); |
| 104 constructorReferences.add(ref); | 103 constructorReferences.add(ref); |
| 105 return ref; | 104 return ref; |
| 106 } | 105 } |
| 107 | 106 |
| 108 void beginNestedDeclaration(String name, {bool hasMembers}) { | 107 void beginNestedDeclaration(String name, {bool hasMembers: true}) { |
| 109 currentDeclaration = new DeclarationBuilder( | 108 currentDeclaration = currentDeclaration.createNested(name, hasMembers); |
| 110 <String, MemberBuilder>{}, name, currentDeclaration); | |
| 111 } | 109 } |
| 112 | 110 |
| 113 DeclarationBuilder<T> endNestedDeclaration() { | 111 DeclarationBuilder<T> endNestedDeclaration() { |
| 114 DeclarationBuilder<T> previous = currentDeclaration; | 112 DeclarationBuilder<T> previous = currentDeclaration; |
| 115 currentDeclaration = currentDeclaration.parent; | 113 currentDeclaration = currentDeclaration.parent; |
| 116 return previous; | 114 return previous; |
| 117 } | 115 } |
| 118 | 116 |
| 119 Uri resolve(String path) => uri.resolve(path); | 117 Uri resolve(String path) => uri.resolve(path); |
| 120 | 118 |
| (...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 227 int charOpenParenOffset, | 225 int charOpenParenOffset, |
| 228 int charEndOffset, | 226 int charEndOffset, |
| 229 String nativeMethodName); | 227 String nativeMethodName); |
| 230 | 228 |
| 231 FormalParameterBuilder addFormalParameter(List<MetadataBuilder> metadata, | 229 FormalParameterBuilder addFormalParameter(List<MetadataBuilder> metadata, |
| 232 int modifiers, T type, String name, bool hasThis, int charOffset); | 230 int modifiers, T type, String name, bool hasThis, int charOffset); |
| 233 | 231 |
| 234 TypeVariableBuilder addTypeVariable(String name, T bound, int charOffset); | 232 TypeVariableBuilder addTypeVariable(String name, T bound, int charOffset); |
| 235 | 233 |
| 236 Builder addBuilder(String name, Builder builder, int charOffset) { | 234 Builder addBuilder(String name, Builder builder, int charOffset) { |
| 237 if (name.indexOf(".") != -1 && name.indexOf("&") == -1) { | |
| 238 addCompileTimeError( | |
| 239 charOffset, | |
| 240 "Only constructors and factories can have" | |
| 241 " names containing a period ('.'): $name"); | |
| 242 } | |
| 243 // TODO(ahe): Set the parent correctly here. Could then change the | 235 // TODO(ahe): Set the parent correctly here. Could then change the |
| 244 // implementation of MemberBuilder.isTopLevel to test explicitly for a | 236 // implementation of MemberBuilder.isTopLevel to test explicitly for a |
| 245 // LibraryBuilder. | 237 // LibraryBuilder. |
| 246 if (currentDeclaration == libraryDeclaration) { | 238 if (currentDeclaration == libraryDeclaration) { |
| 247 if (builder is MemberBuilder) { | 239 if (builder is MemberBuilder) { |
| 248 builder.parent = this; | 240 builder.parent = this; |
| 249 } else if (builder is TypeDeclarationBuilder) { | 241 } else if (builder is TypeDeclarationBuilder) { |
| 250 builder.parent = this; | 242 builder.parent = this; |
| 251 } else if (builder is PrefixBuilder) { | 243 } else if (builder is PrefixBuilder) { |
| 252 assert(builder.parent == this); | 244 assert(builder.parent == this); |
| 253 } else { | 245 } else { |
| 254 return internalError("Unhandled: ${builder.runtimeType}"); | 246 return internalError("Unhandled: ${builder.runtimeType}"); |
| 255 } | 247 } |
| 256 } else { | 248 } else { |
| 257 assert(currentDeclaration.parent == libraryDeclaration); | 249 assert(currentDeclaration.parent == libraryDeclaration); |
| 258 } | 250 } |
| 259 Map<String, Builder> members = currentDeclaration.members; | 251 bool isConstructor = builder is ProcedureBuilder && |
| 252 (builder.isConstructor || builder.isFactory); | |
|
karlklose
2017/04/03 08:05:37
It feels wrong that 'isConstructor' has two differ
ahe
2017/04/04 09:54:51
Perhaps, but on the other hand, kernel uses the na
| |
| 253 Map<String, Builder> members = isConstructor | |
| 254 ? currentDeclaration.constructors | |
| 255 : (builder.isSetter | |
| 256 ? currentDeclaration.setters | |
| 257 : currentDeclaration.members); | |
| 260 Builder existing = members[name]; | 258 Builder existing = members[name]; |
| 261 builder.next = existing; | 259 builder.next = existing; |
| 262 if (builder is PrefixBuilder && existing is PrefixBuilder) { | 260 if (builder is PrefixBuilder && existing is PrefixBuilder) { |
| 263 assert(existing.next == null); | 261 assert(existing.next == null); |
| 264 builder.exports.forEach((String name, Builder builder) { | 262 return existing |
| 265 Builder other = existing.exports.putIfAbsent(name, () => builder); | 263 ..exports.merge(builder.exports, |
| 266 if (other != builder) { | 264 (String name, Builder existing, Builder member) { |
| 267 existing.exports[name] = | 265 return buildAmbiguousBuilder(name, existing, member, charOffset); |
| 268 buildAmbiguousBuilder(name, other, builder, charOffset); | 266 }); |
| 269 } | |
| 270 }); | |
| 271 return existing; | |
| 272 } else if (isDuplicatedDefinition(existing, builder)) { | 267 } else if (isDuplicatedDefinition(existing, builder)) { |
| 273 addCompileTimeError(charOffset, "Duplicated definition of '$name'."); | 268 addCompileTimeError(charOffset, "Duplicated definition of '$name'."); |
| 274 } | 269 } |
| 275 return members[name] = builder; | 270 return members[name] = builder; |
| 276 } | 271 } |
| 277 | 272 |
| 278 bool isDuplicatedDefinition(Builder existing, Builder other) { | 273 bool isDuplicatedDefinition(Builder existing, Builder other) { |
| 279 if (existing == null) return false; | 274 if (existing == null) return false; |
| 280 Builder next = existing.next; | 275 Builder next = existing.next; |
| 281 if (next == null) { | 276 if (next == null) { |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 306 } while (builder != null); | 301 } while (builder != null); |
| 307 }); | 302 }); |
| 308 for (List list in implementationBuilders) { | 303 for (List list in implementationBuilders) { |
| 309 String name = list[0]; | 304 String name = list[0]; |
| 310 Builder builder = list[1]; | 305 Builder builder = list[1]; |
| 311 int charOffset = list[2]; | 306 int charOffset = list[2]; |
| 312 addBuilder(name, builder, charOffset); | 307 addBuilder(name, builder, charOffset); |
| 313 buildBuilder(builder); | 308 buildBuilder(builder); |
| 314 } | 309 } |
| 315 canAddImplementationBuilders = false; | 310 canAddImplementationBuilders = false; |
| 311 | |
| 312 scope.setters.forEach((String name, Builder setter) { | |
| 313 Builder member = scopeBuilder[name]; | |
| 314 if (member == null || !member.isField || member.isFinal) return; | |
| 315 // TODO(ahe): charOffset is missing. | |
| 316 addCompileTimeError( | |
| 317 setter.charOffset, "Conflicts with member '${name}'."); | |
| 318 addCompileTimeError( | |
| 319 member.charOffset, "Conflicts with setter '${name}'."); | |
| 320 }); | |
| 321 | |
| 316 return null; | 322 return null; |
| 317 } | 323 } |
| 318 | 324 |
| 319 void addImplementationBuilder(String name, Builder builder, int charOffset) { | 325 void addImplementationBuilder(String name, Builder builder, int charOffset) { |
| 320 assert(canAddImplementationBuilders, "$uri"); | 326 assert(canAddImplementationBuilders, "$uri"); |
| 321 implementationBuilders.add([name, builder, charOffset]); | 327 implementationBuilders.add([name, builder, charOffset]); |
| 322 } | 328 } |
| 323 | 329 |
| 324 void validatePart() { | 330 void validatePart() { |
| 325 if (parts.isNotEmpty) { | 331 if (parts.isNotEmpty) { |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 370 part.forEach((String name, Builder builder) { | 376 part.forEach((String name, Builder builder) { |
| 371 if (builder.next != null) { | 377 if (builder.next != null) { |
| 372 assert(builder.next.next == null); | 378 assert(builder.next.next == null); |
| 373 addBuilder(name, builder.next, builder.next.charOffset); | 379 addBuilder(name, builder.next, builder.next.charOffset); |
| 374 } | 380 } |
| 375 addBuilder(name, builder, builder.charOffset); | 381 addBuilder(name, builder, builder.charOffset); |
| 376 }); | 382 }); |
| 377 types.addAll(part.types); | 383 types.addAll(part.types); |
| 378 constructorReferences.addAll(part.constructorReferences); | 384 constructorReferences.addAll(part.constructorReferences); |
| 379 part.partOfLibrary = this; | 385 part.partOfLibrary = this; |
| 386 part.scope.becomePartOf(scope); | |
| 380 // TODO(ahe): Include metadata from part? | 387 // TODO(ahe): Include metadata from part? |
| 381 } | 388 } |
| 382 | 389 |
| 383 void buildInitialScopes() { | 390 void buildInitialScopes() { |
| 384 forEach(addToExportScope); | 391 forEach(addToExportScope); |
| 385 forEach((String name, Builder member) { | |
| 386 addToScope(name, member, member.charOffset, false); | |
| 387 }); | |
| 388 } | 392 } |
| 389 | 393 |
| 390 void addImportsToScope() { | 394 void addImportsToScope() { |
| 391 bool explicitCoreImport = this == loader.coreLibrary; | 395 bool explicitCoreImport = this == loader.coreLibrary; |
| 392 for (Import import in imports) { | 396 for (Import import in imports) { |
| 393 if (import.imported == loader.coreLibrary) { | 397 if (import.imported == loader.coreLibrary) { |
| 394 explicitCoreImport = true; | 398 explicitCoreImport = true; |
| 395 } | 399 } |
| 396 import.finalizeImports(this); | 400 import.finalizeImports(this); |
| 397 } | 401 } |
| 398 if (!explicitCoreImport) { | 402 if (!explicitCoreImport) { |
| 399 loader.coreLibrary.exports.forEach((String name, Builder member) { | 403 loader.coreLibrary.exports.forEach((String name, Builder member) { |
| 400 addToScope(name, member, -1, true); | 404 addToScope(name, member, -1, true); |
| 401 }); | 405 }); |
| 402 } | 406 } |
| 403 } | 407 } |
| 404 | 408 |
| 405 @override | 409 @override |
| 406 void addToScope(String name, Builder member, int charOffset, bool isImport) { | 410 void addToScope(String name, Builder member, int charOffset, bool isImport) { |
| 407 Builder existing = scope.lookup(name, member.charOffset, fileUri); | 411 Map<String, Builder> map = |
| 412 member.isSetter ? importScope.setters : importScope.local; | |
| 413 Builder existing = map[name]; | |
| 408 if (existing != null) { | 414 if (existing != null) { |
| 409 if (existing != member) { | 415 if (existing != member) { |
| 410 scope.local[name] = buildAmbiguousBuilder( | 416 map[name] = buildAmbiguousBuilder(name, existing, member, charOffset, |
| 411 name, existing, member, charOffset, | |
| 412 isImport: isImport); | 417 isImport: isImport); |
| 413 } | 418 } |
| 414 } else { | 419 } else { |
| 415 scope.local[name] = member; | 420 map[name] = member; |
| 416 } | 421 } |
| 417 } | 422 } |
| 418 | 423 |
| 419 /// Returns true if the export scope was modified. | 424 /// Returns true if the export scope was modified. |
| 420 bool addToExportScope(String name, Builder member) { | 425 bool addToExportScope(String name, Builder member) { |
| 421 if (name.startsWith("_")) return false; | 426 if (name.startsWith("_")) return false; |
| 422 if (member is PrefixBuilder) return false; | 427 if (member is PrefixBuilder) return false; |
| 423 Builder existing = exports[name]; | 428 Map<String, Builder> map = |
| 429 member.isSetter ? exports.setters : exports.local; | |
| 430 Builder existing = map[name]; | |
| 424 if (existing == member) return false; | 431 if (existing == member) return false; |
| 425 if (existing != null) { | 432 if (existing != null) { |
| 426 Builder result = | 433 Builder result = |
| 427 buildAmbiguousBuilder(name, existing, member, -1, isExport: true); | 434 buildAmbiguousBuilder(name, existing, member, -1, isExport: true); |
| 428 exports[name] = result; | 435 map[name] = result; |
| 429 return result != existing; | 436 return result != existing; |
| 430 } else { | 437 } else { |
| 431 exports[name] = member; | 438 map[name] = member; |
| 432 } | 439 } |
| 433 return true; | 440 return true; |
| 434 } | 441 } |
| 435 | 442 |
| 436 int resolveTypes(_) { | 443 int resolveTypes(_) { |
| 437 int typeCount = types.length; | 444 int typeCount = types.length; |
| 438 for (T t in types) { | 445 for (T t in types) { |
| 439 t.resolveIn(scope); | 446 t.resolveIn(scope); |
| 440 } | 447 } |
| 441 forEach((String name, Builder member) { | 448 forEach((String name, Builder member) { |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 459 String get fullNameForErrors => name ?? "<library '$relativeFileUri'>"; | 466 String get fullNameForErrors => name ?? "<library '$relativeFileUri'>"; |
| 460 } | 467 } |
| 461 | 468 |
| 462 /// Unlike [Scope], this scope is used during construction of builders to | 469 /// Unlike [Scope], this scope is used during construction of builders to |
| 463 /// ensure types and members are added to and resolved in the correct location. | 470 /// ensure types and members are added to and resolved in the correct location. |
| 464 class DeclarationBuilder<T extends TypeBuilder> { | 471 class DeclarationBuilder<T extends TypeBuilder> { |
| 465 final DeclarationBuilder<T> parent; | 472 final DeclarationBuilder<T> parent; |
| 466 | 473 |
| 467 final Map<String, Builder> members; | 474 final Map<String, Builder> members; |
| 468 | 475 |
| 476 final Map<String, Builder> constructors; | |
| 477 | |
| 478 final Map<String, Builder> setters; | |
| 479 | |
| 469 final List<T> types = <T>[]; | 480 final List<T> types = <T>[]; |
| 470 | 481 |
| 471 final String name; | 482 final String name; |
| 472 | 483 |
| 473 final Map<ProcedureBuilder, DeclarationBuilder<T>> factoryDeclarations = | 484 final Map<ProcedureBuilder, DeclarationBuilder<T>> factoryDeclarations; |
| 474 <ProcedureBuilder, DeclarationBuilder<T>>{}; | |
| 475 | 485 |
| 476 DeclarationBuilder(this.members, this.name, [this.parent]); | 486 DeclarationBuilder(this.members, this.setters, this.constructors, |
| 487 this.factoryDeclarations, this.name, this.parent); | |
| 477 | 488 |
| 478 void addMember(String name, MemberBuilder builder) { | 489 DeclarationBuilder.library() |
| 479 if (members == null) { | 490 : this(<String, Builder>{}, <String, Builder>{}, null, null, null, null); |
| 480 parent.addMember(name, builder); | |
| 481 } else { | |
| 482 members[name] = builder; | |
| 483 } | |
| 484 } | |
| 485 | 491 |
| 486 MemberBuilder lookupMember(String name) { | 492 DeclarationBuilder createNested(String name, bool hasMembers) { |
| 487 return members == null ? parent.lookupMember(name) : members[name]; | 493 return new DeclarationBuilder<T>( |
| 494 hasMembers ? <String, MemberBuilder>{} : null, | |
| 495 hasMembers ? <String, MemberBuilder>{} : null, | |
| 496 hasMembers ? <String, MemberBuilder>{} : null, | |
| 497 <ProcedureBuilder, DeclarationBuilder<T>>{}, | |
| 498 name, | |
| 499 this); | |
| 488 } | 500 } |
| 489 | 501 |
| 490 void addType(T type) { | 502 void addType(T type) { |
| 491 types.add(type); | 503 types.add(type); |
| 492 } | 504 } |
| 493 | 505 |
| 494 /// Resolves type variables in [types] and propagate other types to [parent]. | 506 /// Resolves type variables in [types] and propagate other types to [parent]. |
| 495 void resolveTypes( | 507 void resolveTypes( |
| 496 List<TypeVariableBuilder> typeVariables, SourceLibraryBuilder library) { | 508 List<TypeVariableBuilder> typeVariables, SourceLibraryBuilder library) { |
| 497 // TODO(ahe): The input to this method, [typeVariables], shouldn't be just | 509 // TODO(ahe): The input to this method, [typeVariables], shouldn't be just |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 534 types.clear(); | 546 types.clear(); |
| 535 } | 547 } |
| 536 | 548 |
| 537 /// Called to register [procedure] as a factory whose types are collected in | 549 /// Called to register [procedure] as a factory whose types are collected in |
| 538 /// [factoryDeclaration]. Later, once the class has been built, we can | 550 /// [factoryDeclaration]. Later, once the class has been built, we can |
| 539 /// synthesize type variables on the factory matching the class'. | 551 /// synthesize type variables on the factory matching the class'. |
| 540 void addFactoryDeclaration( | 552 void addFactoryDeclaration( |
| 541 ProcedureBuilder procedure, DeclarationBuilder<T> factoryDeclaration) { | 553 ProcedureBuilder procedure, DeclarationBuilder<T> factoryDeclaration) { |
| 542 factoryDeclarations[procedure] = factoryDeclaration; | 554 factoryDeclarations[procedure] = factoryDeclaration; |
| 543 } | 555 } |
| 556 | |
| 557 Scope toScope(Scope parent) { | |
| 558 return new Scope(members, setters, parent, isModifiable: false); | |
| 559 } | |
| 544 } | 560 } |
| OLD | NEW |