| // Copyright (c) 2013, 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. |
| |
| part of dart2js.js_emitter; |
| |
| class MetadataEmitter extends CodeEmitterHelper { |
| /// A list of JS expressions that represent metadata, parameter names and |
| /// type, and return types. |
| final List<String> globalMetadata = []; |
| |
| /// A map used to canonicalize the entries of globalMetadata. |
| final Map<String, int> globalMetadataMap = <String, int>{}; |
| |
| /// The metadata function returns the metadata associated with |
| /// [element] in generated code. The metadata needs to be wrapped |
| /// in a function as it refers to constants that may not have been |
| /// constructed yet. For example, a class is allowed to be |
| /// annotated with itself. The metadata function is used by |
| /// mirrors_patch to implement DeclarationMirror.metadata. |
| jsAst.Fun buildMetadataFunction(Element element) { |
| if (!backend.retainMetadataOf(element)) return null; |
| return compiler.withCurrentElement(element, () { |
| var metadata = []; |
| Link link = element.metadata; |
| // TODO(ahe): Why is metadata sometimes null? |
| if (link != null) { |
| for (; !link.isEmpty; link = link.tail) { |
| MetadataAnnotation annotation = link.head; |
| Constant value = |
| backend.constants.getConstantForMetadata(annotation); |
| if (value == null) { |
| compiler.internalError(annotation, 'Annotation value is null.'); |
| } else { |
| metadata.add(task.constantReference(value)); |
| } |
| } |
| } |
| if (metadata.isEmpty) return null; |
| return js('function() { return # }', |
| new jsAst.ArrayInitializer.from(metadata)); |
| }); |
| } |
| |
| List<int> reifyDefaultArguments(FunctionElement function) { |
| FunctionSignature signature = function.functionSignature; |
| if (signature.optionalParameterCount == 0) return const []; |
| List<int> defaultValues = <int>[]; |
| for (Element element in signature.optionalParameters) { |
| Constant value = backend.constants.getConstantForVariable(element); |
| String stringRepresentation = (value == null) |
| ? "null" |
| : jsAst.prettyPrint(task.constantReference(value), compiler) |
| .getText(); |
| defaultValues.add(addGlobalMetadata(stringRepresentation)); |
| } |
| return defaultValues; |
| } |
| |
| int reifyMetadata(MetadataAnnotation annotation) { |
| Constant value = backend.constants.getConstantForMetadata(annotation); |
| if (value == null) { |
| compiler.internalError(annotation, 'Annotation value is null.'); |
| return -1; |
| } |
| return addGlobalMetadata( |
| jsAst.prettyPrint(task.constantReference(value), compiler).getText()); |
| } |
| |
| int reifyType(DartType type) { |
| jsAst.Expression representation = |
| backend.rti.getTypeRepresentation(type, (variable) { |
| return js.number( |
| task.typeVariableHandler.reifyTypeVariable(variable.element)); |
| }); |
| |
| return addGlobalMetadata( |
| jsAst.prettyPrint(representation, compiler).getText()); |
| } |
| |
| int reifyName(String name) { |
| return addGlobalMetadata('"$name"'); |
| } |
| |
| int addGlobalMetadata(String string) { |
| return globalMetadataMap.putIfAbsent(string, () { |
| globalMetadata.add(string); |
| return globalMetadata.length - 1; |
| }); |
| } |
| |
| void emitMetadata(CodeBuffer buffer) { |
| var literals = backend.typedefTypeLiterals.toList(); |
| Elements.sortedByPosition(literals); |
| var properties = []; |
| for (TypedefElement literal in literals) { |
| var key = namer.getNameX(literal); |
| var value = js.number(reifyType(literal.rawType)); |
| properties.add(new jsAst.Property(js.string(key), value)); |
| } |
| var map = new jsAst.ObjectInitializer(properties); |
| buffer.write( |
| jsAst.prettyPrint( |
| js.statement('init.functionAliases = #', map), compiler)); |
| buffer.write('${N}init.metadata$_=$_['); |
| for (var metadata in globalMetadata) { |
| if (metadata is String) { |
| if (metadata != 'null') { |
| buffer.write(metadata); |
| } |
| } else { |
| throw 'Unexpected value in metadata: ${Error.safeToString(metadata)}'; |
| } |
| buffer.write(',$n'); |
| } |
| buffer.write('];$n'); |
| } |
| |
| List<int> computeMetadata(FunctionElement element) { |
| return compiler.withCurrentElement(element, () { |
| if (!backend.retainMetadataOf(element)) return const <int>[]; |
| List<int> metadata = <int>[]; |
| Link link = element.metadata; |
| // TODO(ahe): Why is metadata sometimes null? |
| if (link != null) { |
| for (; !link.isEmpty; link = link.tail) { |
| metadata.add(reifyMetadata(link.head)); |
| } |
| } |
| return metadata; |
| }); |
| } |
| } |