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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart

Issue 11795002: Revert "Retry "Emit more stuff via ASTs"" (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 11 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 part of js_backend; 5 part of js_backend;
6 6
7 class NativeEmitter { 7 class NativeEmitter {
8 8
9 CodeEmitterTask emitter; 9 CodeEmitterTask emitter;
10 CodeBuffer nativeBuffer; 10 CodeBuffer nativeBuffer;
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
118 String quotedName = cls.nativeTagInfo.slowToString(); 118 String quotedName = cls.nativeTagInfo.slowToString();
119 if (isNativeGlobal(quotedName)) { 119 if (isNativeGlobal(quotedName)) {
120 // Global object, just be like the other types for now. 120 // Global object, just be like the other types for now.
121 return quotedName.substring(3, quotedName.length - 1); 121 return quotedName.substring(3, quotedName.length - 1);
122 } else { 122 } else {
123 return quotedName.substring(2, quotedName.length - 1); 123 return quotedName.substring(2, quotedName.length - 1);
124 } 124 }
125 } 125 }
126 126
127 void generateNativeClass(ClassElement classElement) { 127 void generateNativeClass(ClassElement classElement) {
128 assert(classElement.backendMembers.isEmpty);
129 nativeClasses.add(classElement); 128 nativeClasses.add(classElement);
130 129
131 ClassBuilder builder = new ClassBuilder(); 130 assert(classElement.backendMembers.isEmpty);
132 emitter.emitClassFields(classElement, builder, classIsNative: true); 131 String quotedName = classElement.nativeTagInfo.slowToString();
133 emitter.emitClassGettersSetters(classElement, builder);
134 emitter.emitInstanceMembers(classElement, builder);
135 132
136 // An empty native class may be omitted since the superclass methods can be 133 CodeBuffer fieldBuffer = new CodeBuffer();
137 // located via the dispatch metadata. 134 CodeBuffer getterSetterBuffer = new CodeBuffer();
138 if (builder.properties.isEmpty) return; 135 CodeBuffer methodBuffer = new CodeBuffer();
136
137 emitter.emitClassFields(classElement, fieldBuffer, false,
138 classIsNative: true);
139 emitter.emitClassGettersSetters(classElement, getterSetterBuffer, false);
140 emitter.emitInstanceMembers(classElement, methodBuffer, false);
141
142 if (methodBuffer.isEmpty
143 && fieldBuffer.isEmpty
144 && getterSetterBuffer.isEmpty) {
145 return;
146 }
139 147
140 String nativeTag = toNativeTag(classElement); 148 String nativeTag = toNativeTag(classElement);
141 js.Expression definition = 149 nativeBuffer.add("$defineNativeClassName('$nativeTag',$_");
142 js.call(js.use(defineNativeClassName), 150 nativeBuffer.add('{');
143 [js.string(nativeTag), builder.toObjectInitializer()]); 151 bool firstInMap = true;
144 152 if (!fieldBuffer.isEmpty) {
145 nativeBuffer.add(js.prettyPrint(definition, compiler)); 153 firstInMap = false;
146 nativeBuffer.add('$N$n'); 154 nativeBuffer.add(fieldBuffer);
155 }
156 if (!getterSetterBuffer.isEmpty) {
157 if (!firstInMap) nativeBuffer.add(",");
158 firstInMap = false;
159 nativeBuffer.add("\n$_");
160 nativeBuffer.add(getterSetterBuffer);
161 }
162 if (!methodBuffer.isEmpty) {
163 if (!firstInMap) nativeBuffer.add(",");
164 nativeBuffer.add(methodBuffer);
165 }
166 nativeBuffer.add('$n})$N$n');
147 167
148 classesWithDynamicDispatch.add(classElement); 168 classesWithDynamicDispatch.add(classElement);
149 } 169 }
150 170
151 List<ClassElement> getDirectSubclasses(ClassElement cls) { 171 List<ClassElement> getDirectSubclasses(ClassElement cls) {
152 List<ClassElement> result = directSubtypes[cls]; 172 List<ClassElement> result = directSubtypes[cls];
153 return result == null ? const<ClassElement>[] : result; 173 return result == null ? const<ClassElement>[] : result;
154 } 174 }
155 175
156 void potentiallyConvertDartClosuresToJs(List<js.Statement> statements, 176 void potentiallyConvertDartClosuresToJs(List<js.Statement> statements,
(...skipping 12 matching lines...) Expand all
169 for (js.Parameter stubParameter in stubParameters) { 189 for (js.Parameter stubParameter in stubParameters) {
170 if (stubParameter.name == name) { 190 if (stubParameter.name == name) {
171 DartType type = parameter.computeType(compiler).unalias(compiler); 191 DartType type = parameter.computeType(compiler).unalias(compiler);
172 if (type is FunctionType) { 192 if (type is FunctionType) {
173 // The parameter type is a function type either directly or through 193 // The parameter type is a function type either directly or through
174 // typedef(s). 194 // typedef(s).
175 int arity = type.computeArity(); 195 int arity = type.computeArity();
176 196
177 statements.add( 197 statements.add(
178 new js.ExpressionStatement( 198 new js.ExpressionStatement(
179 js.assign( 199 new js.Assignment(
180 js.use(name), 200 new js.VariableUse(name),
181 js.use(closureConverter).callWith( 201 new js.VariableUse(closureConverter)
182 [js.use(name), new js.LiteralNumber('$arity')])))); 202 .callWith([new js.VariableUse(name),
203 new js.LiteralNumber('$arity')]))));
183 break; 204 break;
184 } 205 }
185 } 206 }
186 } 207 }
187 }); 208 });
188 } 209 }
189 210
190 List<js.Statement> generateParameterStubStatements( 211 List<js.Statement> generateParameterStubStatements(
191 Element member, 212 Element member,
192 String invocationName, 213 String invocationName,
193 List<js.Parameter> stubParameters, 214 List<js.Parameter> stubParameters,
194 List<js.Expression> argumentsBuffer, 215 List<js.Expression> argumentsBuffer,
195 int indexOfLastOptionalArgumentInParameters) { 216 int indexOfLastOptionalArgumentInParameters) {
196 // The target JS function may check arguments.length so we need to 217 // The target JS function may check arguments.length so we need to
197 // make sure not to pass any unspecified optional arguments to it. 218 // make sure not to pass any unspecified optional arguments to it.
198 // For example, for the following Dart method: 219 // For example, for the following Dart method:
199 // foo([x, y, z]); 220 // foo([x, y, z]);
200 // The call: 221 // The call:
201 // foo(y: 1) 222 // foo(y: 1)
202 // must be turned into a JS call to: 223 // must be turned into a JS call to:
203 // foo(null, y). 224 // foo(null, y).
204 225
205 ClassElement classElement = member.enclosingElement; 226 ClassElement classElement = member.enclosingElement;
227 //String nativeTagInfo = classElement.nativeName.slowToString();
206 String nativeTagInfo = classElement.nativeTagInfo.slowToString(); 228 String nativeTagInfo = classElement.nativeTagInfo.slowToString();
207 229
208 List<js.Statement> statements = <js.Statement>[]; 230 List<js.Statement> statements = <js.Statement>[];
209 potentiallyConvertDartClosuresToJs(statements, member, stubParameters); 231 potentiallyConvertDartClosuresToJs(statements, member, stubParameters);
210 232
211 String target; 233 String target;
212 List<js.Expression> arguments; 234 List<js.Expression> arguments;
213 235
214 if (!nativeMethods.contains(member)) { 236 if (!nativeMethods.contains(member)) {
215 // When calling a method that has a native body, we call it with our 237 // When calling a method that has a native body, we call it with our
(...skipping 23 matching lines...) Expand all
239 261
240 // If a method is overridden, we must check if the prototype of 'this' has the 262 // If a method is overridden, we must check if the prototype of 'this' has the
241 // method available. Otherwise, we may end up calling the method from the 263 // method available. Otherwise, we may end up calling the method from the
242 // super class. If the method is not available, we make a direct call to 264 // super class. If the method is not available, we make a direct call to
243 // Object.prototype.$methodName. This method will patch the prototype of 265 // Object.prototype.$methodName. This method will patch the prototype of
244 // 'this' to the real method. 266 // 'this' to the real method.
245 js.Statement generateMethodBodyWithPrototypeCheck( 267 js.Statement generateMethodBodyWithPrototypeCheck(
246 String methodName, 268 String methodName,
247 js.Statement body, 269 js.Statement body,
248 List<js.Parameter> parameters) { 270 List<js.Parameter> parameters) {
249 return js.if_( 271 return new js.If(
250 js.use('Object').dot('getPrototypeOf') 272 new js.VariableUse('Object')
251 .callWith([js.use('this')]) 273 .dot('getPrototypeOf')
252 .dot('hasOwnProperty').callWith([js.string(methodName)]), 274 .callWith([new js.VariableUse('this')])
275 .dot('hasOwnProperty')
276 .callWith([new js.LiteralString("'$methodName'")]),
253 body, 277 body,
254 js.return_( 278 new js.Block(
255 js.use('Object').dot('prototype').dot(methodName).dot('call') 279 <js.Statement>[
256 .callWith( 280 new js.Return(
257 <js.Expression>[js.use('this')]..addAll( 281 new js.VariableUse('Object')
258 parameters.map((param) => js.use(param.name)))))); 282 .dot('prototype').dot(methodName).dot('call')
283 .callWith(
284 <js.Expression>[new js.VariableUse('this')]
285 ..addAll(parameters.map((param) =>
286 new js.VariableUse(param.name)))))
287 ]));
259 } 288 }
260 289
261 js.Block generateMethodBodyWithPrototypeCheckForElement( 290 js.Block generateMethodBodyWithPrototypeCheckForElement(
262 FunctionElement element, 291 FunctionElement element,
263 js.Block body, 292 js.Block body,
264 List<js.Parameter> parameters) { 293 List<js.Parameter> parameters) {
265 String methodName; 294 String methodName;
266 Namer namer = backend.namer; 295 Namer namer = backend.namer;
267 if (element.kind == ElementKind.FUNCTION) { 296 if (element.kind == ElementKind.FUNCTION) {
268 methodName = namer.instanceMethodName(element); 297 methodName = namer.instanceMethodName(element);
269 } else if (element.kind == ElementKind.GETTER) { 298 } else if (element.kind == ElementKind.GETTER) {
270 methodName = namer.getterName(element.getLibrary(), element.name); 299 methodName = namer.getterName(element.getLibrary(), element.name);
271 } else if (element.kind == ElementKind.SETTER) { 300 } else if (element.kind == ElementKind.SETTER) {
272 methodName = namer.setterName(element.getLibrary(), element.name); 301 methodName = namer.setterName(element.getLibrary(), element.name);
273 } else { 302 } else {
274 compiler.internalError("unexpected kind: '${element.kind}'", 303 compiler.internalError('unexpected kind: "${element.kind}"',
275 element: element); 304 element: element);
276 } 305 }
277 306
278 return new js.Block( 307 return new js.Block(
279 [generateMethodBodyWithPrototypeCheck(methodName, body, parameters)]); 308 [generateMethodBodyWithPrototypeCheck(methodName, body, parameters)]);
280 } 309 }
281 310
282 311
283 void emitDynamicDispatchMetadata() { 312 void emitDynamicDispatchMetadata() {
284 if (classesWithDynamicDispatch.isEmpty) return; 313 if (classesWithDynamicDispatch.isEmpty) return;
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
364 varDefns[varName] = existing; 393 varDefns[varName] = existing;
365 tagDefns[tag] = new js.VariableUse(varName); 394 tagDefns[tag] = new js.VariableUse(varName);
366 expressions.add(new js.VariableUse(varName)); 395 expressions.add(new js.VariableUse(varName));
367 } 396 }
368 } 397 }
369 } 398 }
370 } 399 }
371 walk(classElement); 400 walk(classElement);
372 401
373 if (!subtags.isEmpty) { 402 if (!subtags.isEmpty) {
374 expressions.add(js.string(Strings.join(subtags, '|'))); 403 expressions.add(
404 new js.LiteralString("'${Strings.join(subtags, '|')}'"));
375 } 405 }
376 js.Expression expression; 406 js.Expression expression;
377 if (expressions.length == 1) { 407 if (expressions.length == 1) {
378 expression = expressions[0]; 408 expression = expressions[0];
379 } else { 409 } else {
380 js.Expression array = new js.ArrayInitializer.from(expressions); 410 js.Expression array = new js.ArrayInitializer.from(expressions);
381 expression = js.call(array.dot('join'), [js.string('|')]); 411 expression = new js.Call(
412 new js.PropertyAccess.field(array, 'join'),
413 [new js.LiteralString("'|'")]);
382 } 414 }
383 return expression; 415 return expression;
384 } 416 }
385 417
386 for (final ClassElement classElement in preorderDispatchClasses) { 418 for (final ClassElement classElement in preorderDispatchClasses) {
387 tagDefns[classElement] = makeExpression(classElement); 419 tagDefns[classElement] = makeExpression(classElement);
388 } 420 }
389 421
390 // Write out a thunk that builds the metadata. 422 // Write out a thunk that builds the metadata.
391 if (!tagDefns.isEmpty) { 423 if (!tagDefns.isEmpty) {
(...skipping 14 matching lines...) Expand all
406 } 438 }
407 439
408 // [table] is a list of lists, each inner list of the form: 440 // [table] is a list of lists, each inner list of the form:
409 // [dynamic-dispatch-tag, tags-of-classes-implementing-dispatch-tag] 441 // [dynamic-dispatch-tag, tags-of-classes-implementing-dispatch-tag]
410 // E.g. 442 // E.g.
411 // [['Node', 'Text|HTMLElement|HTMLDivElement|...'], ...] 443 // [['Node', 'Text|HTMLElement|HTMLDivElement|...'], ...]
412 js.Expression table = 444 js.Expression table =
413 new js.ArrayInitializer.from( 445 new js.ArrayInitializer.from(
414 preorderDispatchClasses.map((cls) => 446 preorderDispatchClasses.map((cls) =>
415 new js.ArrayInitializer.from([ 447 new js.ArrayInitializer.from([
416 js.string(toNativeTag(cls)), 448 new js.LiteralString("'${toNativeTag(cls)}'"),
417 tagDefns[cls]]))); 449 tagDefns[cls]])));
418 450
419 // $.dynamicSetMetadata(table); 451 // $.dynamicSetMetadata(table);
420 statements.add( 452 statements.add(
421 new js.ExpressionStatement( 453 new js.ExpressionStatement(
422 new js.Call( 454 new js.Call(
423 new js.VariableUse(dynamicSetMetadataName), 455 new js.VariableUse(dynamicSetMetadataName),
424 [table]))); 456 [table])));
425 457
426 // (function(){statements})(); 458 // (function(){statements})();
(...skipping 25 matching lines...) Expand all
452 return subtypes[element] != null; 484 return subtypes[element] != null;
453 } 485 }
454 486
455 bool requiresNativeIsCheck(Element element) { 487 bool requiresNativeIsCheck(Element element) {
456 if (!element.isClass()) return false; 488 if (!element.isClass()) return false;
457 ClassElement cls = element; 489 ClassElement cls = element;
458 if (cls.isNative()) return true; 490 if (cls.isNative()) return true;
459 return isSupertypeOfNativeClass(element); 491 return isSupertypeOfNativeClass(element);
460 } 492 }
461 493
494 void emitIsChecks(Map<String, String> objectProperties) {
495 for (Element element in emitter.checkedClasses) {
496 if (!requiresNativeIsCheck(element)) continue;
497 if (element.isObject(compiler)) continue;
498 String name = backend.namer.operatorIs(element);
499 objectProperties[name] = 'function()$_{${_}return false;$_}';
500 }
501 }
502
462 void assembleCode(CodeBuffer targetBuffer) { 503 void assembleCode(CodeBuffer targetBuffer) {
463 if (nativeClasses.isEmpty) return; 504 if (nativeClasses.isEmpty) return;
464 emitDynamicDispatchMetadata(); 505 emitDynamicDispatchMetadata();
465 targetBuffer.add('$defineNativeClassName = ' 506 targetBuffer.add('$defineNativeClassName = '
466 '$defineNativeClassFunction$N$n'); 507 '$defineNativeClassFunction$N$n');
467 508
468 List<js.Property> objectProperties = <js.Property>[];
469
470 void addProperty(String name, js.Expression value) {
471 objectProperties.add(new js.Property(js.string(name), value));
472 }
473
474 // Because of native classes, we have to generate some is checks 509 // Because of native classes, we have to generate some is checks
475 // by calling a method, instead of accessing a property. So we 510 // by calling a method, instead of accessing a property. So we
476 // attach to the JS Object prototype these methods that return 511 // attach to the JS Object prototype these methods that return
477 // false, and will be overridden by subclasses when they have to 512 // false, and will be overridden by subclasses when they have to
478 // return true. 513 // return true.
479 void emitIsChecks() { 514 Map<String, String> objectProperties = new Map<String, String>();
480 for (Element element in 515 emitIsChecks(objectProperties);
481 Elements.sortedByPosition(emitter.checkedClasses)) {
482 if (!requiresNativeIsCheck(element)) continue;
483 if (element.isObject(compiler)) continue;
484 String name = backend.namer.operatorIs(element);
485 addProperty(name,
486 js.fun([], js.block1(js.return_(new js.LiteralBool(false)))));
487 }
488 }
489 emitIsChecks();
490
491 js.Expression makeCallOnThis(String functionName) =>
492 js.fun([],
493 js.block1(
494 js.return_(
495 js.call(js.use(functionName), [js.use('this')]))));
496 516
497 // In order to have the toString method on every native class, 517 // In order to have the toString method on every native class,
498 // we must patch the JS Object prototype with a helper method. 518 // we must patch the JS Object prototype with a helper method.
499 String toStringName = backend.namer.publicInstanceMethodNameByArity( 519 String toStringName = backend.namer.publicInstanceMethodNameByArity(
500 const SourceString('toString'), 0); 520 const SourceString('toString'), 0);
501 addProperty(toStringName, makeCallOnThis(toStringHelperName)); 521 objectProperties[toStringName] =
522 'function() { return $toStringHelperName(this); }';
502 523
503 // Same as above, but for hashCode. 524 // Same as above, but for hashCode.
504 String hashCodeName = 525 String hashCodeName =
505 backend.namer.publicGetterName(const SourceString('hashCode')); 526 backend.namer.publicGetterName(const SourceString('hashCode'));
506 addProperty(hashCodeName, makeCallOnThis(hashCodeHelperName)); 527 objectProperties[hashCodeName] =
528 'function() { return $hashCodeHelperName(this); }';
507 529
508 // If the native emitter has been asked to take care of the 530 // If the native emitter has been asked to take care of the
509 // noSuchMethod handlers, we do that now. 531 // noSuchMethod handlers, we do that now.
510 if (handleNoSuchMethod) { 532 if (handleNoSuchMethod) {
511 emitter.emitNoSuchMethodHandlers(addProperty); 533 emitter.emitNoSuchMethodHandlers((String name, CodeBuffer buffer) {
534 objectProperties[name] = buffer.toString();
535 });
512 } 536 }
513 537
514 // If we have any properties to add to Object.prototype, we run 538 // If we have any properties to add to Object.prototype, we run
515 // through them and add them using defineProperty. 539 // through them and add them using defineProperty.
516 if (!objectProperties.isEmpty) { 540 if (!objectProperties.isEmpty) {
517 js.Expression init = 541 if (emitter.compiler.enableMinification) targetBuffer.add(";");
518 js.call( 542 targetBuffer.add("(function(table) {\n"
519 js.fun(['table'], 543 " for (var key in table) {\n"
520 js.block1( 544 " $defPropName(Object.prototype, key, table[key]);\n"
521 new js.ForIn( 545 " }\n"
522 new js.VariableDeclarationList( 546 "})({\n");
523 [new js.VariableInitialization( 547 bool first = true;
524 new js.VariableDeclaration('key'), 548 objectProperties.forEach((String name, String function) {
525 null)]), 549 if (!first) targetBuffer.add(",\n");
526 js.use('table'), 550 targetBuffer.add("$_$name:$_$function");
527 new js.ExpressionStatement( 551 first = false;
528 js.call( 552 });
529 js.use(defPropName), 553 targetBuffer.add("\n})$N$n");
530 [js.use('Object').dot('prototype'),
531 js.use('key'),
532 new js.PropertyAccess(js.use('table'),
533 js.use('key'))]))))),
534 [new js.ObjectInitializer(objectProperties)]);
535
536 if (emitter.compiler.enableMinification) targetBuffer.add(';');
537 targetBuffer.add(js.prettyPrint(
538 new js.ExpressionStatement(init), compiler));
539 targetBuffer.add('\n');
540 } 554 }
541
542 targetBuffer.add(nativeBuffer); 555 targetBuffer.add(nativeBuffer);
543 targetBuffer.add('\n'); 556 targetBuffer.add('\n');
544 } 557 }
545 } 558 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698