| Index: sdk/lib/_internal/compiler/implementation/js_emitter/interceptor_emitter.dart
|
| diff --git a/sdk/lib/_internal/compiler/implementation/js_emitter/interceptor_emitter.dart b/sdk/lib/_internal/compiler/implementation/js_emitter/interceptor_emitter.dart
|
| index c57a42c00c01723b33e586d8bbed216864cbc131..b879750bd3c63836a4ae7bd136c88a4549b6a187 100644
|
| --- a/sdk/lib/_internal/compiler/implementation/js_emitter/interceptor_emitter.dart
|
| +++ b/sdk/lib/_internal/compiler/implementation/js_emitter/interceptor_emitter.dart
|
| @@ -29,13 +29,13 @@ class InterceptorEmitter extends CodeEmitterHelper {
|
| void emitGetInterceptorMethod(CodeBuffer buffer,
|
| String key,
|
| Set<ClassElement> classes) {
|
| - jsAst.Statement buildReturnInterceptor(ClassElement cls) {
|
| - return js.return_(js(namer.isolateAccess(cls))['prototype']);
|
| + jsAst.Expression interceptorFor(ClassElement cls) {
|
| + return js('#.prototype', namer.elementAccess(cls));
|
| }
|
|
|
| /**
|
| * Build a JavaScrit AST node for doing a type check on
|
| - * [cls]. [cls] must be an interceptor class.
|
| + * [cls]. [cls] must be a non-native interceptor class.
|
| */
|
| jsAst.Statement buildInterceptorCheck(ClassElement cls) {
|
| jsAst.Expression condition;
|
| @@ -58,7 +58,7 @@ class InterceptorEmitter extends CodeEmitterHelper {
|
| } else {
|
| throw 'internal error';
|
| }
|
| - return js.if_(condition, buildReturnInterceptor(cls));
|
| + return js.statement('if (#) return #', [condition, interceptorFor(cls)]);
|
| }
|
|
|
| bool hasArray = false;
|
| @@ -108,7 +108,7 @@ class InterceptorEmitter extends CodeEmitterHelper {
|
| hasNative = anyNativeClasses;
|
| }
|
|
|
| - jsAst.Block block = new jsAst.Block.empty();
|
| + List<Statement> statements = <Statement>[];
|
|
|
| if (hasNumber) {
|
| jsAst.Statement whenNumber;
|
| @@ -117,75 +117,67 @@ class InterceptorEmitter extends CodeEmitterHelper {
|
| /// and JavaScript's Number (typeof receiver == 'number'). This
|
| /// is the fallback used when we have determined that receiver
|
| /// is a JavaScript Number.
|
| - jsAst.Return returnNumberClass = buildReturnInterceptor(
|
| + jsAst.Expression interceptorForNumber = interceptorFor(
|
| hasDouble ? backend.jsDoubleClass : backend.jsNumberClass);
|
|
|
| if (hasInt) {
|
| - jsAst.Expression isInt = js('Math.floor(receiver) == receiver');
|
| - whenNumber = js.block([
|
| - js.if_(isInt, buildReturnInterceptor(backend.jsIntClass)),
|
| - returnNumberClass]);
|
| + whenNumber = js.statement('''{
|
| + if (Math.floor(receiver) == receiver) return #;
|
| + return #;
|
| + }''', [interceptorFor(backend.jsIntClass), interceptorForNumber]);
|
| } else {
|
| - whenNumber = returnNumberClass;
|
| + whenNumber = js.statement('return #', interceptorForNumber);
|
| }
|
| - block.statements.add(
|
| - js.if_('(typeof receiver) == "number"',
|
| - whenNumber));
|
| + statements.add(
|
| + js.statement('if (typeof receiver == "number") #;', whenNumber));
|
| }
|
|
|
| if (hasString) {
|
| - block.statements.add(buildInterceptorCheck(backend.jsStringClass));
|
| + statements.add(buildInterceptorCheck(backend.jsStringClass));
|
| }
|
| if (hasNull) {
|
| - block.statements.add(buildInterceptorCheck(backend.jsNullClass));
|
| + statements.add(buildInterceptorCheck(backend.jsNullClass));
|
| } else {
|
| // Returning "undefined" or "null" here will provoke a JavaScript
|
| // TypeError which is later identified as a null-error by
|
| // [unwrapException] in js_helper.dart.
|
| - block.statements.add(js.if_('receiver == null',
|
| - js.return_(js('receiver'))));
|
| + statements.add(
|
| + js.statement('if (receiver == null) return receiver'));
|
| }
|
| if (hasBool) {
|
| - block.statements.add(buildInterceptorCheck(backend.jsBoolClass));
|
| + statements.add(buildInterceptorCheck(backend.jsBoolClass));
|
| }
|
| // TODO(ahe): It might be faster to check for Array before
|
| // function and bool.
|
| if (hasArray) {
|
| - block.statements.add(buildInterceptorCheck(backend.jsArrayClass));
|
| + statements.add(buildInterceptorCheck(backend.jsArrayClass));
|
| }
|
|
|
| if (hasNative) {
|
| - block.statements.add(
|
| - js.if_(
|
| - js('(typeof receiver) != "object"'),
|
| - js.return_(js('receiver'))));
|
| -
|
| - // if (receiver instanceof $.Object) return receiver;
|
| - // return $.getNativeInterceptor(receiver);
|
| - block.statements.add(
|
| - js.if_(js('receiver instanceof #',
|
| - js(namer.isolateAccess(compiler.objectClass))),
|
| - js.return_(js('receiver'))));
|
| - block.statements.add(
|
| - js.return_(
|
| - js(namer.isolateAccess(backend.getNativeInterceptorMethod))(
|
| - ['receiver'])));
|
| + statements.add(js.statement(r'''{
|
| + if (typeof receiver != "object") return receiver;
|
| + if (receiver instanceof #) return receiver;
|
| + return #(receiver);
|
| + }''', [
|
| + namer.elementAccess(compiler.objectClass),
|
| + namer.elementAccess(backend.getNativeInterceptorMethod)]));
|
|
|
| } else {
|
| ClassElement jsUnknown = backend.jsUnknownJavaScriptObjectClass;
|
| if (compiler.codegenWorld.instantiatedClasses.contains(jsUnknown)) {
|
| - block.statements.add(
|
| - js.if_(js('!(receiver instanceof #)',
|
| - js(namer.isolateAccess(compiler.objectClass))),
|
| - buildReturnInterceptor(jsUnknown)));
|
| + statements.add(
|
| + js.statement('if (!(receiver instanceof #)) return #;',
|
| + [namer.elementAccess(compiler.objectClass),
|
| + interceptorFor(jsUnknown)]));
|
| }
|
|
|
| - block.statements.add(js.return_(js('receiver')));
|
| + statements.add(js.statement('return receiver'));
|
| }
|
|
|
| buffer.write(jsAst.prettyPrint(
|
| - js('${namer.globalObjectFor(compiler.interceptorsLibrary)}.$key = #',
|
| - js.fun(['receiver'], block)),
|
| + js('''${namer.globalObjectFor(compiler.interceptorsLibrary)}.# =
|
| + function(receiver) { #; }''',
|
| + [key, statements]),
|
| compiler));
|
| buffer.write(N);
|
| }
|
| @@ -208,37 +200,15 @@ class InterceptorEmitter extends CodeEmitterHelper {
|
| // fast path.
|
| jsAst.Statement fastPathForOneShotInterceptor(Selector selector,
|
| Set<ClassElement> classes) {
|
| - jsAst.Expression isNumber(String variable) {
|
| - return js('typeof $variable == "number"');
|
| - }
|
| -
|
| - jsAst.Expression isNotObject(String variable) {
|
| - return js('typeof $variable != "object"');
|
| - }
|
| -
|
| - jsAst.Expression isInt(String variable) {
|
| - return isNumber(variable).binary('&&',
|
| - js('Math.floor($variable) == $variable'));
|
| - }
|
| -
|
| - jsAst.Expression tripleShiftZero(jsAst.Expression receiver) {
|
| - return receiver.binary('>>>', js('0'));
|
| - }
|
|
|
| if (selector.isOperator()) {
|
| String name = selector.name;
|
| if (name == '==') {
|
| - // Unfolds to:
|
| - // if (receiver == null) return a0 == null;
|
| - // if (typeof receiver != 'object') {
|
| - // return a0 != null && receiver === a0;
|
| - // }
|
| - List<jsAst.Statement> body = <jsAst.Statement>[];
|
| - body.add(js.if_('receiver == null', js.return_(js('a0 == null'))));
|
| - body.add(js.if_(
|
| - isNotObject('receiver'),
|
| - js.return_(js('a0 != null && receiver === a0'))));
|
| - return new jsAst.Block(body);
|
| + return js.statement('''{
|
| + if (receiver == null) return a0 == null;
|
| + if (typeof receiver != "object")
|
| + return a0 != null && receiver === a0;
|
| + }''');
|
| }
|
| if (!classes.contains(backend.jsIntClass)
|
| && !classes.contains(backend.jsNumberClass)
|
| @@ -246,29 +216,27 @@ class InterceptorEmitter extends CodeEmitterHelper {
|
| return null;
|
| }
|
| if (selector.argumentCount == 1) {
|
| - // The following operators do not map to a JavaScript
|
| - // operator.
|
| + // The following operators do not map to a JavaScript operator.
|
| if (name == '~/' || name == '<<' || name == '%' || name == '>>') {
|
| return null;
|
| }
|
| - jsAst.Expression result = js('receiver').binary(name, js('a0'));
|
| + jsAst.Expression result = js('receiver $name a0');
|
| if (name == '&' || name == '|' || name == '^') {
|
| - result = tripleShiftZero(result);
|
| + result = js('# >>> 0', result);
|
| }
|
| - // Unfolds to:
|
| - // if (typeof receiver == "number" && typeof a0 == "number")
|
| - // return receiver op a0;
|
| - return js.if_(
|
| - isNumber('receiver').binary('&&', isNumber('a0')),
|
| - js.return_(result));
|
| + return js.statement(
|
| + 'if (typeof receiver == "number" && typeof a0 == "number")'
|
| + ' return #;',
|
| + result);
|
| } else if (name == 'unary-') {
|
| - // [: if (typeof receiver == "number") return -receiver :].
|
| - return js.if_(isNumber('receiver'),
|
| - js.return_(js('-receiver')));
|
| + return js.statement(
|
| + 'if (typeof receiver == "number") return -receiver');
|
| } else {
|
| assert(name == '~');
|
| - return js.if_(isInt('receiver'),
|
| - js.return_(js('~receiver >>> 0')));
|
| + return js.statement('''
|
| + if (typeof receiver == "number" && Math.floor(receiver) == receiver)
|
| + return (~receiver) >>> 0;
|
| + ''');
|
| }
|
| } else if (selector.isIndex() || selector.isIndexSet()) {
|
| // For an index operation, this code generates:
|
| @@ -302,34 +270,33 @@ class InterceptorEmitter extends CodeEmitterHelper {
|
| if (!containsArray && !containsString) {
|
| return null;
|
| }
|
| - jsAst.Expression isIntAndAboveZero = js('a0 >>> 0 === a0');
|
| - jsAst.Expression belowLength = js('a0 < receiver.length');
|
| jsAst.Expression arrayCheck = js('receiver.constructor == Array');
|
| jsAst.Expression indexableCheck =
|
| backend.generateIsJsIndexableCall(js('receiver'), js('receiver'));
|
|
|
| jsAst.Expression orExp(left, right) {
|
| - return left == null ? right : left.binary('||', right);
|
| + return left == null ? right : js('# || #', [left, right]);
|
| }
|
|
|
| if (selector.isIndex()) {
|
| - jsAst.Expression stringCheck = js('typeof receiver == "string"');
|
| jsAst.Expression typeCheck;
|
| if (containsArray) {
|
| typeCheck = arrayCheck;
|
| }
|
|
|
| if (containsString) {
|
| - typeCheck = orExp(typeCheck, stringCheck);
|
| + typeCheck = orExp(typeCheck, js('typeof receiver == "string"'));
|
| }
|
|
|
| if (containsJsIndexable) {
|
| typeCheck = orExp(typeCheck, indexableCheck);
|
| }
|
|
|
| - return js.if_(typeCheck,
|
| - js.if_(isIntAndAboveZero.binary('&&', belowLength),
|
| - js.return_(js('receiver[a0]'))));
|
| + return js.statement('''
|
| + if (#)
|
| + if ((a0 >>> 0) === a0 && a0 < receiver.length)
|
| + return receiver[a0];
|
| + ''', typeCheck);
|
| } else {
|
| jsAst.Expression typeCheck;
|
| if (containsArray) {
|
| @@ -340,11 +307,11 @@ class InterceptorEmitter extends CodeEmitterHelper {
|
| typeCheck = orExp(typeCheck, indexableCheck);
|
| }
|
|
|
| - jsAst.Expression isImmutableArray = typeCheck.binary(
|
| - '&&', js(r'!receiver.immutable$list'));
|
| - return js.if_(isImmutableArray.binary(
|
| - '&&', isIntAndAboveZero.binary('&&', belowLength)),
|
| - js.return_(js('receiver[a0] = a1')));
|
| + return js.statement(r'''
|
| + if (# && !receiver.immutable$list &&
|
| + (a0 >>> 0) === a0 && a0 < receiver.length)
|
| + return receiver[a0] = a1;
|
| + ''', typeCheck);
|
| }
|
| }
|
| return null;
|
| @@ -360,37 +327,30 @@ class InterceptorEmitter extends CodeEmitterHelper {
|
| String getInterceptorName =
|
| namer.getInterceptorName(backend.getInterceptorMethod, classes);
|
|
|
| - List<jsAst.Parameter> parameters = <jsAst.Parameter>[];
|
| - List<jsAst.Expression> arguments = <jsAst.Expression>[];
|
| - parameters.add(new jsAst.Parameter('receiver'));
|
| - arguments.add(js('receiver'));
|
| + List<String> parameterNames = <String>[];
|
| + parameterNames.add('receiver');
|
|
|
| if (selector.isSetter()) {
|
| - parameters.add(new jsAst.Parameter('value'));
|
| - arguments.add(js('value'));
|
| + parameterNames.add('value');
|
| } else {
|
| for (int i = 0; i < selector.argumentCount; i++) {
|
| - String argName = 'a$i';
|
| - parameters.add(new jsAst.Parameter(argName));
|
| - arguments.add(js(argName));
|
| + parameterNames.add('a$i');
|
| }
|
| }
|
|
|
| - List<jsAst.Statement> body = <jsAst.Statement>[];
|
| - jsAst.Statement optimizedPath =
|
| - fastPathForOneShotInterceptor(selector, classes);
|
| - if (optimizedPath != null) {
|
| - body.add(optimizedPath);
|
| - }
|
| -
|
| String invocationName = backend.namer.invocationName(selector);
|
| String globalObject = namer.globalObjectFor(compiler.interceptorsLibrary);
|
| - body.add(js.return_(
|
| - js(globalObject)[getInterceptorName]('receiver')[invocationName](
|
| - arguments)));
|
|
|
| - jsAst.Expression assignment =
|
| - js('${globalObject}.$name = #', js.fun(parameters, body));
|
| + jsAst.Statement optimizedPath =
|
| + fastPathForOneShotInterceptor(selector, classes);
|
| + if (optimizedPath == null) optimizedPath = js.statement(';');
|
| +
|
| + jsAst.Expression assignment = js('${globalObject}.# = function(#) {'
|
| + ' #;'
|
| + ' return #.#(receiver).#(#) }',
|
| + [name, parameterNames,
|
| + optimizedPath,
|
| + globalObject, getInterceptorName, invocationName, parameterNames]);
|
|
|
| buffer.write(jsAst.prettyPrint(assignment, compiler));
|
| buffer.write(N);
|
| @@ -425,7 +385,7 @@ class InterceptorEmitter extends CodeEmitterHelper {
|
| new jsAst.ArrayInitializer(invocationNames.length, elements);
|
|
|
| jsAst.Expression assignment =
|
| - js('${task.isolateProperties}.$name = #', array);
|
| + js('${task.isolateProperties}.# = #', [name, array]);
|
|
|
| buffer.write(jsAst.prettyPrint(assignment, compiler));
|
| buffer.write(N);
|
| @@ -490,7 +450,7 @@ class InterceptorEmitter extends CodeEmitterHelper {
|
| String name =
|
| backend.namer.getNameOfGlobalField(backend.mapTypeToInterceptor);
|
| jsAst.Expression assignment =
|
| - js('${task.isolateProperties}.$name = #', array);
|
| + js('${task.isolateProperties}.# = #', [name, array]);
|
|
|
| buffer.write(jsAst.prettyPrint(assignment, compiler));
|
| buffer.write(N);
|
|
|