blob: 4366c6d7d8da10379fdf02c7ec7396cd4f0a60ec [file] [log] [blame]
// 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>{};
bool mustEmitMetadataFor(Element element) {
return backend.mustRetainMetadata &&
backend.referencedFromMirrorSystem(element);
}
/// 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 (!mustEmitMetadataFor(element)) return null;
return compiler.withCurrentElement(element, () {
List<jsAst.Expression> metadata = <jsAst.Expression>[];
Link link = element.metadata;
// TODO(ahe): Why is metadata sometimes null?
if (link != null) {
for (; !link.isEmpty; link = link.tail) {
MetadataAnnotation annotation = link.head;
ConstantExpression constant =
backend.constants.getConstantForMetadata(annotation);
if (constant == null) {
compiler.internalError(annotation, 'Annotation value is null.');
} else {
metadata.add(emitter.constantReference(constant.value));
}
}
}
if (metadata.isEmpty) return null;
return js('function() { return # }',
new jsAst.ArrayInitializer(metadata));
});
}
List<int> reifyDefaultArguments(FunctionElement function) {
FunctionSignature signature = function.functionSignature;
if (signature.optionalParameterCount == 0) return const [];
List<int> defaultValues = <int>[];
for (ParameterElement element in signature.optionalParameters) {
ConstantExpression constant =
backend.constants.getConstantForVariable(element);
String stringRepresentation = (constant == null)
? "null"
: jsAst.prettyPrint(
emitter.constantReference(constant.value), compiler).getText();
defaultValues.add(addGlobalMetadata(stringRepresentation));
}
return defaultValues;
}
int reifyMetadata(MetadataAnnotation annotation) {
ConstantExpression constant =
backend.constants.getConstantForMetadata(annotation);
if (constant == null) {
compiler.internalError(annotation, 'Annotation value is null.');
return -1;
}
return addGlobalMetadata(
jsAst.prettyPrint(
emitter.constantReference(constant.value), compiler).getText());
}
int reifyType(DartType type) {
jsAst.Expression representation =
backend.rti.getTypeRepresentation(
type,
(variable) {
return js.number(
emitter.typeVariableHandler.reifyTypeVariable(
variable.element));
},
(TypedefType typedef) {
return backend.isAccessibleByReflection(typedef.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(CodeOutput output) {
String metadataAccess = emitter.generateEmbeddedGlobalAccessString(
embeddedNames.METADATA);
output.add('$metadataAccess$_=$_[');
for (String metadata in globalMetadata) {
if (metadata is String) {
if (metadata != 'null') {
output.add(metadata);
}
} else {
throw 'Unexpected value in metadata: ${Error.safeToString(metadata)}';
}
output.add(',$n');
}
output.add('];$n');
}
List<int> computeMetadata(FunctionElement element) {
return compiler.withCurrentElement(element, () {
if (!mustEmitMetadataFor(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;
});
}
}