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

Side by Side Diff: lib/compiler/implementation/emitter.dart

Issue 10310060: Generate getters and setters dynamically. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Rebase Created 8 years, 7 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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 /** 5 /**
6 * A function element that represents a closure call. The signature is copied 6 * A function element that represents a closure call. The signature is copied
7 * from the given element. 7 * from the given element.
8 */ 8 */
9 class ClosureInvocationElement extends FunctionElement { 9 class ClosureInvocationElement extends FunctionElement {
10 ClosureInvocationElement(SourceString name, 10 ClosureInvocationElement(SourceString name,
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
46 String get finishClassesName() 46 String get finishClassesName()
47 => '${namer.ISOLATE}.\$finishClasses'; 47 => '${namer.ISOLATE}.\$finishClasses';
48 String get finishIsolateConstructorName() 48 String get finishIsolateConstructorName()
49 => '${namer.ISOLATE}.\$finishIsolateConstructor'; 49 => '${namer.ISOLATE}.\$finishIsolateConstructor';
50 String get pendingClassesName() 50 String get pendingClassesName()
51 => '${namer.ISOLATE}.\$pendingClasses'; 51 => '${namer.ISOLATE}.\$pendingClasses';
52 String get isolatePropertiesName() 52 String get isolatePropertiesName()
53 => '${namer.ISOLATE}.${namer.ISOLATE_PROPERTIES}'; 53 => '${namer.ISOLATE}.${namer.ISOLATE_PROPERTIES}';
54 54
55 String get defineClassFunction() { 55 String get defineClassFunction() {
56 final String GETTER = "getter";
57 final String SETTER = "setter";
56 // First the class name, then the super class name, followed by the fields 58 // First the class name, then the super class name, followed by the fields
57 // (in an array) and the members (inside an Object literal). 59 // (in an array) and the members (inside an Object literal).
58 // The caller can also pass in the constructor as a function if needed. 60 // The caller can also pass in the constructor as a function if needed.
59 // 61 //
60 // Example: 62 // Example:
61 // defineClass("A", "B", ["x", "y"], { 63 // defineClass("A", "B", ["x", "y"], {
62 // foo$1: function(y) { 64 // foo$1: function(y) {
63 // print(this.x + y); 65 // print(this.x + y);
64 // }, 66 // },
65 // bar$2: function(t, v) { 67 // bar$2: function(t, v) {
66 // this.x = t - v; 68 // this.x = t - v;
67 // }, 69 // },
68 // }); 70 // });
69 return """ 71 return """
70 function(cls, superclass, fields, prototype) { 72 function(cls, superclass, fields, fieldBitSet, prototype) {
ngeoffray 2012/05/09 08:50:28 Not sure we need the two following lines. They kin
floitsch 2012/05/09 13:58:36 inlined.
73 ${namer.getDynamicFieldGetterNamerFunction(GETTER)}
74 ${namer.getDynamicFieldSetterNamerFunction(SETTER)}
71 var constructor; 75 var constructor;
72 if (typeof fields == 'function') { 76 if (typeof fields == 'function') {
73 constructor = fields; 77 constructor = fields;
78 prototype = fieldBitSet;
ngeoffray 2012/05/09 08:50:28 This is starting to be confusing. Maybe add anothe
floitsch 2012/05/09 13:58:36 removed.
74 } else { 79 } else {
80 if (typeof fieldBitSet !== 'number') {
81 prototype = fieldBitSet;
82 fieldBitSet = 0;
83 }
75 var str = "(function " + cls + "("; 84 var str = "(function " + cls + "(";
76 var body = ""; 85 var body = "";
77 for (var i = 0; i < fields.length; i++) { 86 for (var i = 0; i < fields.length; i++) {
kasperl 2012/05/09 07:53:15 Have you thought about encoding the need for a get
floitsch 2012/05/09 13:58:36 Done.
78 if (i != 0) str += ", "; 87 if (i != 0) str += ", ";
79 str += fields[i]; 88 var field = fields[i];
89 str += field;
80 body += "this." + fields[i] + " = " + fields[i] + ";\\n"; 90 body += "this." + fields[i] + " = " + fields[i] + ";\\n";
kasperl 2012/05/09 07:53:15 Use field instead of fields[i].
floitsch 2012/05/09 13:58:36 Done.
91 if (fieldBitSet & 1) {
92 var getterString = "return this." + field + ";";
93 prototype[$GETTER(field)] = new Function(getterString);
94 }
95 fieldBitSet >>>= 1;
96 if (fieldBitSet & 1) {
97 var setterString = "this." + field + " = v;";
98 prototype[$SETTER(field)] = new Function("v", setterString);
99 }
100 fieldBitSet >>>= 1;
81 } 101 }
82 str += ") {" + body + "})"; 102 str += ") {" + body + "})";
83 constructor = eval(str); 103 constructor = eval(str);
sra1 2012/05/08 17:59:42 Should this new new Function too?
floitsch 2012/05/09 13:58:36 I would like it to be a "new Function", but then w
84 } 104 }
85 $isolatePropertiesName[cls] = constructor; 105 $isolatePropertiesName[cls] = constructor;
86 constructor.prototype = prototype; 106 constructor.prototype = prototype;
87 if (superclass !== "") { 107 if (superclass !== "") {
88 $pendingClassesName[cls] = superclass; 108 $pendingClassesName[cls] = superclass;
89 } 109 }
90 }"""; 110 }""";
91 } 111 }
92 112
93 String get finishClassesFunction() { 113 String get finishClassesFunction() {
(...skipping 225 matching lines...) Expand 10 before | Expand all | Expand 10 after
319 void defineInstanceMember(String invocationName, 339 void defineInstanceMember(String invocationName,
320 String definition)) { 340 String definition)) {
321 Set<Selector> selectors = compiler.universe.invokedNames[member.name]; 341 Set<Selector> selectors = compiler.universe.invokedNames[member.name];
322 if (selectors == null) return; 342 if (selectors == null) return;
323 for (Selector selector in selectors) { 343 for (Selector selector in selectors) {
324 if (!selector.applies(member, compiler)) continue; 344 if (!selector.applies(member, compiler)) continue;
325 addParameterStub(member, selector, defineInstanceMember); 345 addParameterStub(member, selector, defineInstanceMember);
326 } 346 }
327 } 347 }
328 348
349 bool instanceFieldNeedsGetter(Element member) {
350 assert(member.kind === ElementKind.FIELD);
351 return compiler.universe.hasGetter(member, compiler);
352 }
353
354 bool instanceFieldNeedsSetter(Element member) {
355 assert(member.kind === ElementKind.FIELD);
356 return (member.modifiers === null || !member.modifiers.isFinal()) &&
357 compiler.universe.hasSetter(member, compiler);
kasperl 2012/05/09 07:53:15 The indentation seems a bit off here.
floitsch 2012/05/09 13:58:36 Done.
358 }
359
360 String compiledFieldName(Element member) {
361 assert(member.kind === ElementKind.FIELD);
362 return member.isNative()
363 ? member.name.slowToString()
364 : namer.getName(member);
365 }
366
329 void addInstanceMember(Element member, 367 void addInstanceMember(Element member,
368 Set<Element> dynamicallyEmittedFieldGettersSetters,
330 void defineInstanceMember(String invocationName, 369 void defineInstanceMember(String invocationName,
331 String definition)) { 370 String definition)) {
332 // TODO(floitsch): we don't need to deal with members of 371 // TODO(floitsch): we don't need to deal with members of
333 // uninstantiated classes, that have been overwritten by subclasses. 372 // uninstantiated classes, that have been overwritten by subclasses.
334 373
335 if (member.kind === ElementKind.FUNCTION 374 if (member.kind === ElementKind.FUNCTION
336 || member.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY 375 || member.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY
337 || member.kind === ElementKind.GETTER 376 || member.kind === ElementKind.GETTER
338 || member.kind === ElementKind.SETTER) { 377 || member.kind === ElementKind.SETTER) {
339 if (member.modifiers !== null && member.modifiers.isAbstract()) return; 378 if (member.modifiers !== null && member.modifiers.isAbstract()) return;
340 String codeBlock = compiler.universe.generatedCode[member]; 379 String codeBlock = compiler.universe.generatedCode[member];
341 if (codeBlock == null) return; 380 if (codeBlock == null) return;
342 defineInstanceMember(namer.getName(member), codeBlock); 381 defineInstanceMember(namer.getName(member), codeBlock);
343 codeBlock = compiler.universe.generatedBailoutCode[member]; 382 codeBlock = compiler.universe.generatedBailoutCode[member];
344 if (codeBlock !== null) { 383 if (codeBlock !== null) {
345 defineInstanceMember(compiler.namer.getBailoutName(member), codeBlock); 384 defineInstanceMember(compiler.namer.getBailoutName(member), codeBlock);
346 } 385 }
347 FunctionElement function = member; 386 FunctionElement function = member;
348 FunctionSignature parameters = function.computeSignature(compiler); 387 FunctionSignature parameters = function.computeSignature(compiler);
349 if (!parameters.optionalParameters.isEmpty()) { 388 if (!parameters.optionalParameters.isEmpty()) {
350 addParameterStubs(member, defineInstanceMember); 389 addParameterStubs(member, defineInstanceMember);
351 } 390 }
352 } else if (member.kind === ElementKind.FIELD) { 391 } else if (member.kind === ElementKind.FIELD) {
353 // TODO(ngeoffray): Have another class generate the code for the 392 // TODO(ngeoffray): Have another class generate the code for the
354 // fields. 393 // fields.
ngeoffray 2012/05/09 08:50:28 Remove the TODO.
floitsch 2012/05/09 13:58:36 Done.
355 if ((member.modifiers === null || !member.modifiers.isFinal()) && 394 if (instanceFieldNeedsGetter(member) &&
356 compiler.universe.hasSetter(member, compiler)) { 395 !dynamicallyEmittedFieldGettersSetters.contains(member)) {
396 String getterName = namer.getterName(member.getLibrary(), member.name);
397 String name = compiledFieldName(member);
398 defineInstanceMember(getterName, "function() { return this.$name; }");
399 }
400
401 if (instanceFieldNeedsSetter(member) &&
402 !dynamicallyEmittedFieldGettersSetters.contains(member)) {
357 String setterName = namer.setterName(member.getLibrary(), member.name); 403 String setterName = namer.setterName(member.getLibrary(), member.name);
358 String name = member.isNative() 404 String name = compiledFieldName(member);
359 ? member.name.slowToString()
360 : namer.getName(member);
361 defineInstanceMember(setterName, "function(v) { this.$name = v; }"); 405 defineInstanceMember(setterName, "function(v) { this.$name = v; }");
362 } 406 }
363 if (compiler.universe.hasGetter(member, compiler)) {
364 String getterName = namer.getterName(member.getLibrary(), member.name);
365 String name = member.isNative()
366 ? member.name.slowToString()
367 : namer.getName(member);
368 defineInstanceMember(getterName, "function() { return this.$name; }");
369 }
370 } else { 407 } else {
371 compiler.internalError('unexpected kind: "${member.kind}"', 408 compiler.internalError('unexpected kind: "${member.kind}"',
372 element: member); 409 element: member);
373 } 410 }
374 emitExtraAccessors(member, defineInstanceMember); 411 emitExtraAccessors(member, defineInstanceMember);
375 } 412 }
376 413
377 List<String> generateFieldList(ClassElement classElement) {
378 List<String> result = <String>[];
379 void addField(ClassElement enclosingClass, Element member) {
380 result.add(namer.instanceFieldName(member.getLibrary(), member.name));
381 }
382
383 classElement.forEachInstanceField(addField,
384 includeBackendMembers: true,
385 includeSuperMembers: true);
386 return result;
387 }
388
389 void generateClass(ClassElement classElement, StringBuffer buffer) { 414 void generateClass(ClassElement classElement, StringBuffer buffer) {
390 needsDefineClass = true; 415 needsDefineClass = true;
391 416
392 if (classElement.isNative()) { 417 if (classElement.isNative()) {
393 nativeEmitter.generateNativeClass(classElement); 418 nativeEmitter.generateNativeClass(classElement);
394 return; 419 return;
395 } else { 420 } else {
396 // TODO(ngeoffray): Instead of switching between buffer, we 421 // TODO(ngeoffray): Instead of switching between buffer, we
397 // should create code sections, and decide where to emit them at 422 // should create code sections, and decide where to emit them at
398 // the end. 423 // the end.
399 buffer = mainBuffer; 424 buffer = mainBuffer;
400 } 425 }
401 426
402 String className = namer.getName(classElement); 427 String className = namer.getName(classElement);
403 ClassElement superclass = classElement.superclass; 428 ClassElement superclass = classElement.superclass;
404 String superName = ""; 429 String superName = "";
405 if (superclass !== null) { 430 if (superclass !== null) {
406 superName = namer.getName(superclass); 431 superName = namer.getName(superclass);
407 } 432 }
408 String constructorName = namer.safeName(classElement.name.slowToString()); 433 String constructorName = namer.safeName(classElement.name.slowToString());
434
435 Set<Element> dynamicallyEmittedGettersSetters = new Set<Element>();
436
409 buffer.add('$defineClassName("$className", "$superName", '); 437 buffer.add('$defineClassName("$className", "$superName", ');
438
410 // If the class is never instantiated we still need to set it up for 439 // If the class is never instantiated we still need to set it up for
411 // inheritance purposes, but we can simplify its JavaScript constructor. 440 // inheritance purposes, but we can simplify its JavaScript constructor.
412 if (!compiler.universe.instantiatedClasses.contains(classElement)) { 441 bool isInstantiated =
413 buffer.add("[]"); 442 compiler.universe.instantiatedClasses.contains(classElement);
414 } else { 443
415 List<String> fields = generateFieldList(classElement); 444 // We encode 15 field getters and setters in an integer bit field. This way
416 buffer.add('['); 445 // the integer will fit into a JavaScript Smi.
417 for (int i = 0; i < fields.length; i++) { 446 // The getters and setters for the marked fields will be generated
418 if (i != 0) buffer.add(", "); 447 // dynamically.
419 buffer.add('"${fields[i]}"'); 448 final int MAX_DYNAMICALLY_GENERATED_FIELD_GETTERS_SETTERS = 15;
449 int fieldBits = 0;
450 int fieldCounter = 0;
451 buffer.add('[');
452
453 void addField(ClassElement enclosingClass, Element member) {
454 assert(!member.isNative());
455 LibraryElement library = member.getLibrary();
456 SourceString name = member.name;
457 String fieldName = namer.instanceFieldName(library, name);
458 // See, if we can dynamically create getters and setters.
kasperl 2012/05/09 07:53:15 Remove , after See.
floitsch 2012/05/09 13:58:36 Done.
459 // We can only generate getters and setters for [classElement] since
460 // the fields of super classes could be overwritten with getters or
461 // setters.
462 bool needsDynamicGetter = false;
463 bool needsDynamicSetter = false;
464 if (fieldCounter <= MAX_DYNAMICALLY_GENERATED_FIELD_GETTERS_SETTERS &&
465 enclosingClass === classElement) {
466 needsDynamicGetter = instanceFieldNeedsGetter(member);
467 needsDynamicSetter = instanceFieldNeedsSetter(member);
468 // Make sure that the name we would generate dynamically matches the
469 // name we assign to the getter/setter during compilation time.
470 if ((needsDynamicGetter && namer.getterName(library, name) !=
kasperl 2012/05/09 07:53:15 Maybe add a helper on namer for this check (takes
floitsch 2012/05/09 13:58:36 code removed.
471 namer.dynamicGetterName(fieldName))
472 || (needsDynamicSetter && namer.setterName(library, name) !=
473 namer.dynamicSetterName(fieldName))) {
474 needsDynamicGetter = false;
475 needsDynamicSetter = false;
476 }
420 } 477 }
421 buffer.add(']'); 478
479 if (isInstantiated || needsDynamicGetter || needsDynamicSetter) {
480 fieldCounter++;
481 if (fieldCounter != 1) buffer.add(", ");
482 buffer.add('"$fieldName"');
483 if (needsDynamicGetter || needsDynamicSetter) {
484 dynamicallyEmittedGettersSetters.add(member);
485 if (needsDynamicGetter) {
486 fieldBits |= 1 << ((fieldCounter - 1) * 2);
487 }
488 if (needsDynamicSetter) {
489 fieldBits |= 1 << ((fieldCounter - 1) * 2 + 1);
490 }
491 }
492 }
422 } 493 }
494
495 // If a class is not instantiated then we add the field just so we can
496 // generate the field getter/setter dynamically. Since this is only
497 // allowed on fields that are in [classElement] we don't need to visit
498 // superclasses for non-instantiated classes.
499 classElement.forEachInstanceField(addField,
500 includeBackendMembers: true,
501 includeSuperMembers: isInstantiated);
502 buffer.add(']');
503 if (fieldBits != 0) buffer.add(', $fieldBits');
423 buffer.add(', {\n'); 504 buffer.add(', {\n');
424 505
425 void defineInstanceMember(String name, String value) { 506 void defineInstanceMember(String name, String value) {
426 buffer.add(' $name: $value,\n'); 507 buffer.add(' $name: $value,\n');
427 } 508 }
428 509
429 classElement.forEachMember(includeBackendMembers: true, 510 classElement.forEachMember(includeBackendMembers: true,
430 f: (ClassElement enclosing, Element member) { 511 f: (ClassElement enclosing, Element member) {
431 if (member.isInstanceMember()) { 512 if (member.isInstanceMember()) {
432 addInstanceMember(member, defineInstanceMember); 513 addInstanceMember(
514 member, dynamicallyEmittedGettersSetters, defineInstanceMember);
433 } 515 }
434 }); 516 });
435 517
436 generateTypeTests(classElement, (Element other) { 518 generateTypeTests(classElement, (Element other) {
437 if (nativeEmitter.requiresNativeIsCheck(other)) { 519 if (nativeEmitter.requiresNativeIsCheck(other)) {
438 defineInstanceMember(namer.operatorIs(other), 520 defineInstanceMember(namer.operatorIs(other),
439 'function() { return true; }'); 521 'function() { return true; }');
440 } else { 522 } else {
441 defineInstanceMember(namer.operatorIs(other), 'true'); 523 defineInstanceMember(namer.operatorIs(other), 'true');
442 } 524 }
(...skipping 460 matching lines...) Expand 10 before | Expand all | Expand 10 after
903 mainBuffer.add('function init() {\n'); 985 mainBuffer.add('function init() {\n');
904 mainBuffer.add(' $isolateProperties = {};\n'); 986 mainBuffer.add(' $isolateProperties = {};\n');
905 addDefineClassAndFinishClassFunctionsIfNecessary(mainBuffer); 987 addDefineClassAndFinishClassFunctionsIfNecessary(mainBuffer);
906 emitFinishIsolateConstructor(mainBuffer); 988 emitFinishIsolateConstructor(mainBuffer);
907 mainBuffer.add('}\n'); 989 mainBuffer.add('}\n');
908 compiler.assembledCode = mainBuffer.toString(); 990 compiler.assembledCode = mainBuffer.toString();
909 }); 991 });
910 return compiler.assembledCode; 992 return compiler.assembledCode;
911 } 993 }
912 } 994 }
OLDNEW
« no previous file with comments | « no previous file | lib/compiler/implementation/namer.dart » ('j') | lib/compiler/implementation/namer.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698