| // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| library kernel.binary.loader; |
| |
| import '../repository.dart'; |
| import '../ast.dart'; |
| import 'tag.dart'; |
| import 'dart:io'; |
| import 'ast_from_binary.dart'; |
| import 'package:path/path.dart' as pathlib; |
| |
| abstract class BinaryReferenceLoader { |
| Library getLibraryReference(Library from, String relativePath); |
| Class getClassReference(Library library, int tag, int index); |
| Member getMemberReference(TreeNode classOrLibrary, int tag, int index); |
| Member getLibraryMemberReference(Library library, int tag, int index); |
| Member getClassMemberReference(Class classNode, int tag, int index); |
| } |
| |
| class BinaryLoader implements BinaryReferenceLoader { |
| final Repository repository; |
| |
| BinaryLoader(this.repository); |
| |
| Library getLibraryReference(Library from, String relativePath) { |
| var fullUri = from.importUri.resolve(relativePath); |
| return repository.getLibraryReference(fullUri); |
| } |
| |
| static int _pow2roundup(int x) { |
| --x; |
| x |= x >> 1; |
| x |= x >> 2; |
| x |= x >> 4; |
| x |= x >> 8; |
| x |= x >> 16; |
| return x + 1; |
| } |
| |
| TreeNode _extendList( |
| TreeNode parent, List<TreeNode> items, int index, TreeNode build()) { |
| if (items.length <= index) { |
| // Avoid excessive resizing by growing the list in steps. |
| items.length = _pow2roundup(index + 1); |
| } |
| return items[index] ??= build()..parent = parent; |
| } |
| |
| Class getClassReference(Library library, int tag, int index) { |
| return _extendList( |
| library, library.classes, index, () => _buildClassReference(tag)); |
| } |
| |
| Class _buildClassReference(int tag) { |
| switch (tag) { |
| case Tag.NormalClassReference: |
| case Tag.NormalClass: |
| return new NormalClass(null); |
| |
| case Tag.MixinClassReference: |
| case Tag.MixinClass: |
| return new MixinClass(null, null); |
| default: |
| throw 'Invalid class reference tag: $tag'; |
| } |
| } |
| |
| Field _buildFieldReference() { |
| return new Field(null); |
| } |
| |
| Constructor _buildConstructorReference() { |
| return new Constructor(null); |
| } |
| |
| Procedure _buildProcedureReference() { |
| return new Procedure(null, null, null); |
| } |
| |
| Member getMemberReference(TreeNode classOrLibrary, int tag, int index) { |
| if (classOrLibrary is Class) { |
| return getClassMemberReference(classOrLibrary, tag, index); |
| } else { |
| return getLibraryMemberReference(classOrLibrary, tag, index); |
| } |
| } |
| |
| Member getLibraryMemberReference(Library library, int tag, int index) { |
| switch (tag) { |
| case Tag.LibraryFieldReference: |
| case Tag.Field: |
| return _extendList( |
| library, library.fields, index, _buildFieldReference); |
| case Tag.LibraryProcedureReference: |
| case Tag.Procedure: |
| return _extendList( |
| library, library.procedures, index, _buildProcedureReference); |
| default: |
| throw 'Invalid library member reference tag: $tag'; |
| } |
| } |
| |
| Member getClassMemberReference(Class classNode, int tag, int index) { |
| switch (tag) { |
| case Tag.ClassFieldReference: |
| case Tag.Field: |
| NormalClass class_ = classNode; |
| return _extendList( |
| classNode, class_.fields, index, _buildFieldReference); |
| case Tag.ClassConstructorReference: |
| case Tag.Constructor: |
| return _extendList(classNode, classNode.constructors, index, |
| _buildConstructorReference); |
| case Tag.ClassProcedureReference: |
| case Tag.Procedure: |
| NormalClass class_ = classNode; |
| return _extendList( |
| classNode, class_.procedures, index, _buildProcedureReference); |
| default: |
| throw 'Invalid library member reference tag: $tag'; |
| } |
| } |
| |
| void ensureLibraryIsLoaded(Library node) { |
| if (node.isLoaded) return; |
| _buildLibraryBody(node); |
| node.isLoaded = true; |
| } |
| |
| void ensureClassIsLoaded(Class classNode) { |
| ensureLibraryIsLoaded(classNode.enclosingLibrary); |
| } |
| |
| void ensureMemberIsLoaded(Member member) { |
| ensureLibraryIsLoaded(member.enclosingLibrary); |
| } |
| |
| /// Replaces the .dart extension with .bart. |
| String _translateFilename(String filename) { |
| if (filename.endsWith('.dart')) { |
| return pathlib.withoutExtension(filename) + '.bart'; |
| } else { |
| return filename; |
| } |
| } |
| |
| File _getFileForUri(Uri uri) { |
| var filename = _translateFilename(repository.resolveUri(uri)); |
| var file = new File(filename); |
| if (!file.existsSync()) { |
| throw 'Could not find a .bart file for URI "$uri" at $filename. ' |
| 'Compiling from both .dart and .bart files is not supported.'; |
| } |
| return file; |
| } |
| |
| void _buildLibraryBody(Library node) { |
| var file = _getFileForUri(node.importUri); |
| var bytes = file.readAsBytesSync(); |
| new BinaryBuilder(this, bytes, file.path).readLibraryFile(node); |
| } |
| |
| Program loadProgram(String filename) { |
| var bytes = new File(filename).readAsBytesSync(); |
| return new BinaryBuilder(this, bytes, filename).readProgramFile(); |
| } |
| |
| TreeNode loadProgramOrLibrary(String path) { |
| var uri = repository.normalizePath(path); |
| var file = _getFileForUri(uri); |
| var bytes = file.readAsBytesSync(); |
| return new BinaryBuilder(this, bytes, file.path).readProgramOrLibraryFile( |
| () => repository.getLibraryReference(uri)); |
| } |
| } |