Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(281)

Unified Diff: frog/leg/compile_time_constants.dart

Issue 9753001: super and this in compile-time constants. (Closed) Base URL: /home/flo/programming/dart2/../dart/dart@master
Patch Set: Remove work-around for VM bug. Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | frog/leg/compiler.dart » ('j') | tests/language/src/CompileTimeConstantITest.dart » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: frog/leg/compile_time_constants.dart
diff --git a/frog/leg/compile_time_constants.dart b/frog/leg/compile_time_constants.dart
index bee8dfaf6a812bd6fde67c4ee25ac446a7279845..63e60558b8214aab864fbfa5104575a6a515629b 100644
--- a/frog/leg/compile_time_constants.dart
+++ b/frog/leg/compile_time_constants.dart
@@ -683,8 +683,8 @@ class CompileTimeConstantEvaluator extends AbstractVisitor {
CompileTimeConstantEvaluator.insideConstructor(this.constantHandler,
ngeoffray 2012/03/26 07:38:31 Because you can fetch constantHandler from the com
floitsch 2012/03/27 00:52:50 Done.
this.elements,
- this.compiler,
- this.definitions);
+ this.compiler)
+ : definitions = new Map<Element, Constant>();
bool insideConstructor() => definitions !== null;
@@ -933,121 +933,190 @@ class CompileTimeConstantEvaluator extends AbstractVisitor {
error(node);
}
- Constant visitNewExpression(NewExpression node) {
- Element currentElement = compiler.currentElement;
-
- void assignArgumentsToParameters(
- FunctionParameters parameters,
- Map<Element, Constant> constructorDefinitions,
- Map<Element, Constant> fieldValues) {
- Send send = node.send;
- if (send.arguments.isEmpty() && parameters.parameterCount == 0) return;
- List<Constant> arguments = <Constant>[];
- Selector selector = elements.getSelector(send);
-
- Function compileArgument = evaluate;
- Function compileConstant = constantHandler.compileVariable;
- bool succeeded = selector.addSendArgumentsToList(
- send, arguments, parameters, compileArgument, compileConstant);
- if (!succeeded) error(node);
-
- int index = 0;
- parameters.forEachParameter((Element parameter) {
- Constant argument = arguments[index++];
- constructorDefinitions[parameter] = argument;
- if (parameter.kind == ElementKind.FIELD_PARAMETER) {
- FieldParameterElement fieldParameterElement = parameter;
- fieldValues[fieldParameterElement.fieldElement] = argument;
- }
- });
+ /** Returns the list of constants that are passed to the constructor. */
+ List<Constant> evaluateConstructorArguments(Send send,
+ FunctionElement constructor) {
+ FunctionParameters parameters = constructor.computeParameters(compiler);
+ if (send.arguments.isEmpty() && parameters.parameterCount == 0) {
ngeoffray 2012/03/26 07:38:31 Is that premature optimization? I would really get
ahe 2012/03/26 08:03:25 Note that these kind of hacks are not necessary if
floitsch 2012/03/27 00:52:50 Done.
+ return const <Constant>[];
}
+ List<Constant> arguments = <Constant>[];
+ Selector selector = elements.getSelector(send);
+
+ Function compileArgument = evaluate;
floitsch 2012/03/23 23:15:57 The VM has/had a bug here (see previous uploads),
floitsch 2012/03/27 00:52:50 Fix has been committed.
+ Function compileConstant = compiler.compileVariable;
+ bool succeeded = selector.addSendArgumentsToList(
+ send, arguments, parameters, compileArgument, compileConstant);
+ if (!succeeded) error(send);
+ return arguments;
+ }
+
+ /**
+ * Given the arguments (a list of constants) assigns the to the parameters,
ngeoffray 2012/03/26 07:38:31 the to the -> ?
floitsch 2012/03/27 00:52:50 Done.
+ * updating the definitions map. If the constructor has field-initializer
+ * parameters (like [:this.x:]), also updates the [fieldValues] map.
+ */
+ void assignConstructorArgumentsToParameters(
+ FunctionElement constructor, List<Constant> arguments,
ngeoffray 2012/03/26 07:38:31 one argument per line please
floitsch 2012/03/27 00:52:50 Done.
+ Map<Element, Constant> fieldValues) {
+ // Assign arguments to parameters.
+ FunctionParameters parameters = constructor.computeParameters(compiler);
+ int index = 0;
+ parameters.forEachParameter((Element parameter) {
+ Constant argument = arguments[index++];
+ definitions[parameter] = argument;
+ if (parameter.kind == ElementKind.FIELD_PARAMETER) {
+ FieldParameterElement fieldParameterElement = parameter;
+ fieldValues[fieldParameterElement.fieldElement] = argument;
+ }
+ });
+ }
- void compileInitializers(Link<Node> initializers,
- CompileTimeConstantEvaluator evaluator,
- TreeElements constructorElements,
- Map<Element, Constant> constructorDefinitions,
- Map<Element, Constant> fieldValues) {
- for (Link<Node> link = initializers; !link.isEmpty(); link = link.tail) {
+ /**
+ * Runs through the initializers of the given [constructor] and updates
+ * the [fieldValues] map.
+ *
+ * The [executedConstructors] set is used to detect cycles in redirection
+ * constructors.
+ */
+ void evaluateConstructorInitializers(
+ FunctionElement constructor,
+ Set<FunctionElement> executedConstructors,
+ Map<Element, Constant> fieldValues) {
+
+ void evaluateSuperOrRedirectSend(FunctionElement targetConstructor,
+ List<Constant> targetArguments) {
+ compiler.withCurrentElement(targetConstructor, () {
+ evaluateConstructorFieldValues(
+ targetConstructor, targetArguments, executedConstructors,
+ fieldValues);
+ });
+ }
+
+ FunctionExpression functionNode = constructor.parseNode(compiler);
+ NodeList initializerList = functionNode.initializers;
+
+ bool foundSuperOrRedirect = false;
+
+ if (initializerList !== null) {
+ for (Link<Node> link = initializerList.nodes;
+ !link.isEmpty();
+ link = link.tail) {
assert(link.head is Send);
if (link.head is !SendSet) {
// A super initializer or constructor redirection.
Send call = link.head;
- assert(Initializers.isSuperConstructorCall(call) ||
- Initializers.isConstructorRedirect(call));
- compiler.unimplemented("ConstantHandler with this or super",
- node: call);
+ FunctionElement targetConstructor = elements[call];
+ List<Constant> targetArguments =
+ evaluateConstructorArguments(call, targetConstructor);
+ evaluateSuperOrRedirectSend(targetConstructor, targetArguments);
+ foundSuperOrRedirect = true;
} else {
// A field initializer.
SendSet init = link.head;
- Link<Node> arguments = init.arguments;
- assert(!arguments.isEmpty() && arguments.tail.isEmpty());
- Constant fieldValue = evaluator.evaluate(arguments.head);
- fieldValues[constructorElements[init]] = fieldValue;
+ Link<Node> initArguments = init.arguments;
+ assert(!initArguments.isEmpty() && initArguments.tail.isEmpty());
+ Constant fieldValue = evaluate(initArguments.head);
+ fieldValues[elements[init]] = fieldValue;
}
}
}
+ if (!foundSuperOrRedirect) {
+ // No super initializer found. Try to find the default constructor if
+ // the class is not Object.
+ ClassElement enclosingClass = constructor.enclosingElement;
+ ClassElement superClass = enclosingClass.superclass;
+ if (enclosingClass != compiler.objectClass) {
+ assert(superClass !== null);
+ assert(superClass.isResolved);
+ FunctionElement targetConstructor =
+ superClass.lookupConstructor(superClass.name);
+ if (targetConstructor === null) {
+ compiler.internalError("no default constructor available");
+ }
+ evaluateSuperOrRedirectSend(targetConstructor, const <Constant>[]);
+ }
+ }
+ }
+
+ /**
+ * Simulates the execution of the given [constructor] with the given
+ * [arguments] to obtain the field values that need to be passed to the
+ * native JavaScript constructor.
+ *
+ * The [executedConstructors] set is used to detect cycles in redirection
+ * constructors.
ngeoffray 2012/03/26 07:38:31 Having to also check it here is quite annoying. We
floitsch 2012/03/27 00:52:50 You are right. This is already handled by the reso
+ */
+ void evaluateConstructorFieldValues(FunctionElement constructor,
+ List<Constant> arguments,
+ Set<FunctionElement> executedConstructors,
+ Map<Element, Constant> fieldValues) {
+ compiler.withCurrentElement(constructor, () {
+ if (executedConstructors.contains(constructor)) {
+ MessageKind kind = MessageKind.REDIRECTING_CONSTRUCTOR_CYCLE;
+ compiler.reportError(constructor.parseNode(compiler),
+ new CompileTimeConstantError(kind, const []));
+ }
+ executedConstructors.add(constructor);
+
+ TreeElements constructorElements =
+ compiler.resolver.resolveMethodElement(constructor);
+ CompileTimeConstantEvaluator evaluator =
+ new CompileTimeConstantEvaluator.insideConstructor(
+ constantHandler, constructorElements, compiler);
+
+ evaluator.assignConstructorArgumentsToParameters(
+ constructor, arguments, fieldValues);
+ evaluator.evaluateConstructorInitializers(
+ constructor, executedConstructors, fieldValues);
+ });
+ }
+
+ Constant visitNewExpression(NewExpression node) {
List<Constant> buildJsNewArguments(ClassElement classElement,
Map<Element, Constant> fieldValues) {
ngeoffray 2012/03/26 07:38:31 I think it would be easier to read if this functio
floitsch 2012/03/27 00:52:50 Done.
List<Constant> jsNewArguments = <Constant>[];
- for (Element member in classElement.members) {
- if (member.isInstanceMember() && member.kind == ElementKind.FIELD) {
- Constant fieldValue = fieldValues[member];
- if (fieldValue === null) {
- // Use the default value.
- fieldValue = constantHandler.compileVariable(member);
+ // TODO(floitsch): share this code with the emitter, so that we don't
+ // need to care about the order of fields here.
+ while (classElement != compiler.objectClass) {
+ for (Element member in classElement.members) {
+ if (member.isInstanceMember() && member.kind == ElementKind.FIELD) {
+ Constant fieldValue = fieldValues[member];
+ if (fieldValue === null) {
+ // Use the default value.
+ fieldValue = constantHandler.compileVariable(member);
+ }
+ jsNewArguments.add(fieldValue);
}
- jsNewArguments.add(fieldValue);
}
- }
- if (classElement.superclass != compiler.coreLibrary.find(Types.OBJECT)) {
- compiler.withCurrentElement(currentElement, () {
- compiler.unimplemented("ConstantHandler with super", node: node);
- });
- }
+ classElement = classElement.superclass;
+ }
return jsNewArguments;
}
if (!node.isConst()) error(node);
FunctionElement constructor = elements[node.send];
- TreeElements constructorElements =
- compiler.resolver.resolveMethodElement(constructor);
- if (constructor != constructor.defaultImplementation) {
+ ClassElement classElement = constructor.enclosingElement;
+ if (classElement.isInterface()) {
+ compiler.resolver.resolveMethodElement(constructor);
constructor = constructor.defaultImplementation;
- constructorElements =
- compiler.resolver.resolveMethodElement(constructor);
+ classElement = constructor.enclosingElement;
}
List<Constant> jsNewArguments;
- ClassElement classElement = constructor.enclosingElement;
compiler.withCurrentElement(constructor, () {
- FunctionExpression functionNode = constructor.parseNode(compiler);
- NodeList initializerList = functionNode.initializers;
- FunctionParameters parameters = constructor.computeParameters(compiler);
-
+ // We use a set to detect cycles in redirection constructors.
+ Set<FunctionElement> executedConstructors = new Set<FunctionElement>();
Map<Element, Constant> fieldValues = new Map<Element, Constant>();
ngeoffray 2012/03/26 07:38:31 Would it make sense to have an abstraction for a c
floitsch 2012/03/27 00:52:50 Made ConstructorEvaluator a subclass.
- Map<Element, Constant> constructorDefinitions =
- new Map<Element, Constant>();
-
- assignArgumentsToParameters(parameters, constructorDefinitions,
- fieldValues);
- CompileTimeConstantEvaluator initializerEvaluator =
- new CompileTimeConstantEvaluator.insideConstructor(
- constantHandler, constructorElements, compiler,
- constructorDefinitions);
- if (initializerList !== null) {
- Link<Node> initializers = functionNode.initializers.nodes;
- compileInitializers(initializers,
- initializerEvaluator,
- constructorElements,
- constructorDefinitions,
- fieldValues);
- }
+ List<Constant> arguments =
+ evaluateConstructorArguments(node.send, constructor);
+ evaluateConstructorFieldValues(
+ constructor, arguments, executedConstructors, fieldValues);
jsNewArguments = buildJsNewArguments(classElement, fieldValues);
});
-
compiler.registerInstantiatedClass(classElement);
// TODO(floitsch): take generic types into account.
Type type = classElement.computeType(compiler);
« no previous file with comments | « no previous file | frog/leg/compiler.dart » ('j') | tests/language/src/CompileTimeConstantITest.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698