| Index: sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart
|
| diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart b/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart
|
| index 84e57f1402a2c844a2f8a37cdea20866df370c59..f51edba43a1a1d099484f37b7f686a6edce5b67c 100644
|
| --- a/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart
|
| +++ b/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart
|
| @@ -125,45 +125,25 @@ function(cls, desc) {
|
| }
|
|
|
| void generateNativeClass(ClassElement classElement) {
|
| - nativeClasses.add(classElement);
|
| -
|
| assert(classElement.backendMembers.isEmpty);
|
| - String quotedName = classElement.nativeTagInfo.slowToString();
|
| -
|
| - CodeBuffer fieldBuffer = new CodeBuffer();
|
| - CodeBuffer getterSetterBuffer = new CodeBuffer();
|
| - CodeBuffer methodBuffer = new CodeBuffer();
|
| + nativeClasses.add(classElement);
|
|
|
| - emitter.emitClassFields(classElement, fieldBuffer, false,
|
| - classIsNative: true);
|
| - emitter.emitClassGettersSetters(classElement, getterSetterBuffer, false);
|
| - emitter.emitInstanceMembers(classElement, methodBuffer, false);
|
| + ClassBuilder builder = new ClassBuilder();
|
| + emitter.emitClassFields(classElement, builder, classIsNative: true);
|
| + emitter.emitClassGettersSetters(classElement, builder);
|
| + emitter.emitInstanceMembers(classElement, builder);
|
|
|
| - if (methodBuffer.isEmpty
|
| - && fieldBuffer.isEmpty
|
| - && getterSetterBuffer.isEmpty) {
|
| - return;
|
| - }
|
| + // An empty native class may be omitted since the superclass methods can be
|
| + // located via the dispatch metadata.
|
| + if (builder.properties.isEmpty) return;
|
|
|
| String nativeTag = toNativeTag(classElement);
|
| - nativeBuffer.add("$defineNativeClassName('$nativeTag',$_");
|
| - nativeBuffer.add('{');
|
| - bool firstInMap = true;
|
| - if (!fieldBuffer.isEmpty) {
|
| - firstInMap = false;
|
| - nativeBuffer.add(fieldBuffer);
|
| - }
|
| - if (!getterSetterBuffer.isEmpty) {
|
| - if (!firstInMap) nativeBuffer.add(",");
|
| - firstInMap = false;
|
| - nativeBuffer.add("\n$_");
|
| - nativeBuffer.add(getterSetterBuffer);
|
| - }
|
| - if (!methodBuffer.isEmpty) {
|
| - if (!firstInMap) nativeBuffer.add(",");
|
| - nativeBuffer.add(methodBuffer);
|
| - }
|
| - nativeBuffer.add('$n})$N$n');
|
| + js.Expression definition =
|
| + js.call(js.use(defineNativeClassName),
|
| + [js.string(nativeTag), builder.toObjectInitializer()]);
|
| +
|
| + nativeBuffer.add(js.prettyPrint(definition, compiler));
|
| + nativeBuffer.add('$N$n');
|
|
|
| classesWithDynamicDispatch.add(classElement);
|
| }
|
| @@ -196,11 +176,10 @@ function(cls, desc) {
|
|
|
| statements.add(
|
| new js.ExpressionStatement(
|
| - new js.Assignment(
|
| - new js.VariableUse(name),
|
| - new js.VariableUse(closureConverter)
|
| - .callWith([new js.VariableUse(name),
|
| - new js.LiteralNumber('$arity')]))));
|
| + js.assign(
|
| + js.use(name),
|
| + js.use(closureConverter).callWith(
|
| + [js.use(name), new js.LiteralNumber('$arity')]))));
|
| break;
|
| }
|
| }
|
| @@ -224,7 +203,6 @@ function(cls, desc) {
|
| // foo(null, y).
|
|
|
| ClassElement classElement = member.enclosingElement;
|
| - //String nativeTagInfo = classElement.nativeName.slowToString();
|
| String nativeTagInfo = classElement.nativeTagInfo.slowToString();
|
|
|
| List<js.Statement> statements = <js.Statement>[];
|
| @@ -268,23 +246,16 @@ function(cls, desc) {
|
| String methodName,
|
| js.Statement body,
|
| List<js.Parameter> parameters) {
|
| - return new js.If(
|
| - new js.VariableUse('Object')
|
| - .dot('getPrototypeOf')
|
| - .callWith([new js.VariableUse('this')])
|
| - .dot('hasOwnProperty')
|
| - .callWith([new js.LiteralString("'$methodName'")]),
|
| + return js.if_(
|
| + js.use('Object').dot('getPrototypeOf')
|
| + .callWith([js.use('this')])
|
| + .dot('hasOwnProperty').callWith([js.string(methodName)]),
|
| body,
|
| - new js.Block(
|
| - <js.Statement>[
|
| - new js.Return(
|
| - new js.VariableUse('Object')
|
| - .dot('prototype').dot(methodName).dot('call')
|
| - .callWith(
|
| - <js.Expression>[new js.VariableUse('this')]
|
| - ..addAll(parameters.map((param) =>
|
| - new js.VariableUse(param.name)))))
|
| - ]));
|
| + js.return_(
|
| + js.use('Object').dot('prototype').dot(methodName).dot('call')
|
| + .callWith(
|
| + <js.Expression>[js.use('this')]..addAll(
|
| + parameters.map((param) => js.use(param.name))))));
|
| }
|
|
|
| js.Block generateMethodBodyWithPrototypeCheckForElement(
|
| @@ -300,7 +271,7 @@ function(cls, desc) {
|
| } else if (element.kind == ElementKind.SETTER) {
|
| methodName = namer.setterName(element.getLibrary(), element.name);
|
| } else {
|
| - compiler.internalError('unexpected kind: "${element.kind}"',
|
| + compiler.internalError("unexpected kind: '${element.kind}'",
|
| element: element);
|
| }
|
|
|
| @@ -400,17 +371,14 @@ function(cls, desc) {
|
| walk(classElement);
|
|
|
| if (!subtags.isEmpty) {
|
| - expressions.add(
|
| - new js.LiteralString("'${Strings.join(subtags, '|')}'"));
|
| + expressions.add(js.string(Strings.join(subtags, '|')));
|
| }
|
| js.Expression expression;
|
| if (expressions.length == 1) {
|
| expression = expressions[0];
|
| } else {
|
| js.Expression array = new js.ArrayInitializer.from(expressions);
|
| - expression = new js.Call(
|
| - new js.PropertyAccess.field(array, 'join'),
|
| - [new js.LiteralString("'|'")]);
|
| + expression = js.call(array.dot('join'), [js.string('|')]);
|
| }
|
| return expression;
|
| }
|
| @@ -445,7 +413,7 @@ function(cls, desc) {
|
| new js.ArrayInitializer.from(
|
| preorderDispatchClasses.map((cls) =>
|
| new js.ArrayInitializer.from([
|
| - new js.LiteralString("'${toNativeTag(cls)}'"),
|
| + js.string(toNativeTag(cls)),
|
| tagDefns[cls]])));
|
|
|
| // $.dynamicSetMetadata(table);
|
| @@ -491,67 +459,86 @@ function(cls, desc) {
|
| return isSupertypeOfNativeClass(element);
|
| }
|
|
|
| - void emitIsChecks(Map<String, String> objectProperties) {
|
| - for (Element element in emitter.checkedClasses) {
|
| - if (!requiresNativeIsCheck(element)) continue;
|
| - if (element.isObject(compiler)) continue;
|
| - String name = backend.namer.operatorIs(element);
|
| - objectProperties[name] = 'function()$_{${_}return false;$_}';
|
| - }
|
| - }
|
| -
|
| void assembleCode(CodeBuffer targetBuffer) {
|
| if (nativeClasses.isEmpty) return;
|
| emitDynamicDispatchMetadata();
|
| targetBuffer.add('$defineNativeClassName = '
|
| '$defineNativeClassFunction$N$n');
|
|
|
| + List<js.Property> objectProperties = <js.Property>[];
|
| +
|
| + void addProperty(String name, js.Expression value) {
|
| + objectProperties.add(new js.Property(js.string(name), value));
|
| + }
|
| +
|
| // Because of native classes, we have to generate some is checks
|
| // by calling a method, instead of accessing a property. So we
|
| // attach to the JS Object prototype these methods that return
|
| // false, and will be overridden by subclasses when they have to
|
| // return true.
|
| - Map<String, String> objectProperties = new Map<String, String>();
|
| - emitIsChecks(objectProperties);
|
| + void emitIsChecks() {
|
| + for (Element element in
|
| + Elements.sortedByPosition(emitter.checkedClasses)) {
|
| + if (!requiresNativeIsCheck(element)) continue;
|
| + if (element.isObject(compiler)) continue;
|
| + String name = backend.namer.operatorIs(element);
|
| + addProperty(name,
|
| + js.fun([], js.block1(js.return_(new js.LiteralBool(false)))));
|
| + }
|
| + }
|
| + emitIsChecks();
|
| +
|
| + js.Expression makeCallOnThis(String functionName) =>
|
| + js.fun([],
|
| + js.block1(
|
| + js.return_(
|
| + js.call(js.use(functionName), [js.use('this')]))));
|
|
|
| // In order to have the toString method on every native class,
|
| // we must patch the JS Object prototype with a helper method.
|
| String toStringName = backend.namer.publicInstanceMethodNameByArity(
|
| const SourceString('toString'), 0);
|
| - objectProperties[toStringName] =
|
| - 'function() { return $toStringHelperName(this); }';
|
| + addProperty(toStringName, makeCallOnThis(toStringHelperName));
|
|
|
| // Same as above, but for hashCode.
|
| String hashCodeName =
|
| backend.namer.publicGetterName(const SourceString('hashCode'));
|
| - objectProperties[hashCodeName] =
|
| - 'function() { return $hashCodeHelperName(this); }';
|
| + addProperty(hashCodeName, makeCallOnThis(hashCodeHelperName));
|
|
|
| // If the native emitter has been asked to take care of the
|
| // noSuchMethod handlers, we do that now.
|
| if (handleNoSuchMethod) {
|
| - emitter.emitNoSuchMethodHandlers((String name, CodeBuffer buffer) {
|
| - objectProperties[name] = buffer.toString();
|
| - });
|
| + emitter.emitNoSuchMethodHandlers(addProperty);
|
| }
|
|
|
| // If we have any properties to add to Object.prototype, we run
|
| // through them and add them using defineProperty.
|
| if (!objectProperties.isEmpty) {
|
| - if (emitter.compiler.enableMinification) targetBuffer.add(";");
|
| - targetBuffer.add("(function(table) {\n"
|
| - " for (var key in table) {\n"
|
| - " $defPropName(Object.prototype, key, table[key]);\n"
|
| - " }\n"
|
| - "})({\n");
|
| - bool first = true;
|
| - objectProperties.forEach((String name, String function) {
|
| - if (!first) targetBuffer.add(",\n");
|
| - targetBuffer.add("$_$name:$_$function");
|
| - first = false;
|
| - });
|
| - targetBuffer.add("\n})$N$n");
|
| + js.Expression init =
|
| + js.call(
|
| + js.fun(['table'],
|
| + js.block1(
|
| + new js.ForIn(
|
| + new js.VariableDeclarationList(
|
| + [new js.VariableInitialization(
|
| + new js.VariableDeclaration('key'),
|
| + null)]),
|
| + js.use('table'),
|
| + new js.ExpressionStatement(
|
| + js.call(
|
| + js.use(defPropName),
|
| + [js.use('Object').dot('prototype'),
|
| + js.use('key'),
|
| + new js.PropertyAccess(js.use('table'),
|
| + js.use('key'))]))))),
|
| + [new js.ObjectInitializer(objectProperties)]);
|
| +
|
| + if (emitter.compiler.enableMinification) targetBuffer.add(';');
|
| + targetBuffer.add(js.prettyPrint(
|
| + new js.ExpressionStatement(init), compiler));
|
| + targetBuffer.add('\n');
|
| }
|
| +
|
| targetBuffer.add(nativeBuffer);
|
| targetBuffer.add('\n');
|
| }
|
|
|