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

Unified Diff: sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart

Issue 12499005: dart2js: Create noSuchMethod handlers at runtime to reduce overhead. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Reordered some stuff due to code review feedback Created 7 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
Index: sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart b/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
index 8d4ba2e1a54d92268aecf2af9bd776e7c3c8313c..5cafedb63a0c84b936ae181ece2257341c08045c 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
@@ -69,6 +69,7 @@ class CodeEmitterTask extends CompilerTask {
final List<ClassElement> regularClasses = <ClassElement>[];
final List<ClassElement> deferredClasses = <ClassElement>[];
final List<ClassElement> nativeClasses = <ClassElement>[];
+ final List<Selector> trivialNsmHandlers = <Selector>[];
// TODO(ngeoffray): remove this field.
Set<ClassElement> instantiatedClasses;
@@ -277,9 +278,9 @@ class CodeEmitterTask extends CompilerTask {
js['var body = ""'],
// for (var i = 0; i < fields.length; i++) {
- js.for_(js['var i = 0'], js['i < fields.length'], js['i++'], [
+ js.for_('var i = 0', 'i < fields.length', 'i++', [
// if (i != 0) str += ", ";
- js.if_(js['i != 0'], js['str += ", "']),
+ js.if_('i != 0', js['str += ", "']),
js['var field = fields[i]'],
js['field = generateAccessor(field, prototype)'],
@@ -332,6 +333,223 @@ class CodeEmitterTask extends CompilerTask {
];
}
+ const MAX_MINIFIED_LENGTH_FOR_DIFF_ENCODING = 4;
+
+ // If we need fewer than this many noSuchMethod handlers we can save space by
ngeoffray 2013/03/13 15:03:27 indentation off by one
erikcorry 2013/03/14 10:06:57 Fixed
+ // just emitting them in JS, rather than emitting the JS needed to generate
+ // them at run time.
+ const VERY_FEW_NO_SUCH_METHOD_HANDLERS = 10;
+
+ /**
+ * Adds (at runtime) the handlers to the Object class which catch calls to
+ * methods that the object does not have. The handlers create an invocation
+ * mirror object.
+ *
+ * The current version only gives you the minified name when minifying (when
+ * not minifying this method is not called).
+ *
+ * In order to generate the noSuchMethod handlers we only need the minified
+ * name of the method. We test the first character of the minified name to
+ * determine if it is a getter or a setter, and we use the arguments array at
+ * runtime to get the number of arguments and their values. If the method
+ * involves named arguments etc. then we don't handle it here, but emit the
+ * handler method directly on the Object class.
+ *
+ * The minified names are mostly 1-4 character names, which we emit in sorted
+ * order (primary key is length, secondary ordering is lexicographic). This
+ * gives an order like ... dD dI dX da ...
+ *
+ * Gzip is good with repeated text, but it can't diff-encode, so we do that
+ * for it. We encode the minified names in a comma-separated string, but all
+ * the 1-4 character names are encoded before the first comma as a series of
+ * base 26 numbers. The last digit of each number is lower case, the others
+ * are upper case, so 1 is "b" and 26 is "Ba".
+ *
+ * We think of the minified names as base 88 numbers using the ASCII
+ * characters from # to z. The base 26 numbers each encode the delta from
+ * the previous minified name to the next. So if there is a minified name
+ * called Df and the next is Dh, then they are 2971 and 2973 when thought of
+ * as base 88 numbers. The difference is 2, which is "c" in lower-case-
+ * terminated base 26.
+ *
+ * The reason we don't encode long minified names with this method is that
+ * decoding the base 88 numbers would overflow JavaScript's puny integers.
+ *
+ * There are some selectors that have a special calling convention (because
+ * they are called with the receiver as the first argument). They need a
+ * slightly different noSuchMethod handler, so we handle these first.
+ */
+ void addTrivialNsmHandlers(List<jsAst.Node> statements) {
+ if (trivialNsmHandlers.length == 0) return;
+ // Sort by calling convention, JS name length and by JS name.
+ trivialNsmHandlers.sort((a, b) {
+ bool aIsIntercepted = backend.isInterceptedName(a.name);
+ bool bIsIntercepted = backend.isInterceptedName(b.name);
+ if (aIsIntercepted != bIsIntercepted) return aIsIntercepted ? -1 : 1;
+ String aName = namer.invocationMirrorInternalName(a);
+ String bName = namer.invocationMirrorInternalName(b);
+ if (aName.length != bName.length) return aName.length - bName.length;
+ return aName.compareTo(bName);
+ });
+
+ // Find out how many selectors there are with the special calling
+ // convention.
+ int firstNormalSelector = trivialNsmHandlers.length;
+ for (int i = 0; i < trivialNsmHandlers.length; i++) {
+ if (!backend.isInterceptedName(trivialNsmHandlers[i].name)) {
+ firstNormalSelector = i;
+ break;
+ }
+ }
+
+ // Get the short names (JS names, perhaps minified).
+ Iterable<String> shorts = trivialNsmHandlers.map((selector) =>
+ namer.invocationMirrorInternalName(selector));
+ final diffShorts = <String>[];
+ var diffEncoding = new StringBuffer();
+
+ // Treat string as a number in base 88 with digits in ASCII order from # to
+ // z. The short name sorting is based on length, and uses ASCII order for
+ // equal length strings so this means that names are ascending. The hash
+ // character, #, is never given as input, but we need it because it's the
+ // implicit leading zero (otherwise we could not code names with leading
+ // dollar signs).
+ int fromBase88(String x) {
+ int answer = 0;
+ for (int i = 0; i < x.length; i++) {
+ int c = x.codeUnitAt(i);
+ // No support for Unicode minified identifiers in JS.
+ assert(c >= $$ && c <= $z);
+ answer *= 88;
+ answer += c - $HASH;
+ }
+ return answer;
+ }
+
+ // Big endian encoding, A = 0, B = 1, etc. terminate with lower case.
ngeoffray 2013/03/13 15:03:27 terminate -> terminates
erikcorry 2013/03/14 10:06:57 This was an imperative verb, not a singular one.
+ String toBase26(int x) {
+ int c = x;
+ var encodingChars = <int>[];
+ encodingChars.add($a + (c % 26));
+ while (true) {
+ c ~/= 26;
+ if (c == 0) break;
+ encodingChars.add($A + (c % 26));
+ }
+ return new String.fromCharCodes(encodingChars.reversed.toList());
+ }
+
+ bool minify = compiler.enableMinification;
ngeoffray 2013/03/13 15:03:27 Isn't that always true in this method?
erikcorry 2013/03/14 10:06:57 No, but we only diff encode if we are minifying.
+ bool useDiffEncoding = minify && shorts.length > 30;
+
+ int previous = 0;
+ int nameCounter = 0;
+ for (String short in shorts) {
+ // Emit period that resets the diff base to zero when we switch to normal
+ // calling convention (this avoids the need to code negative diffs).
+ if (useDiffEncoding && nameCounter == firstNormalSelector) {
+ diffEncoding.write(".");
+ previous = 0;
+ }
+ if (short.length <= MAX_MINIFIED_LENGTH_FOR_DIFF_ENCODING &&
+ useDiffEncoding) {
+ int base63 = fromBase88(short);
+ int diff = base63 - previous;
+ previous = base63;
+ String base26Diff = toBase26(diff);
+ diffEncoding.write(base26Diff);
+ } else {
+ if (useDiffEncoding || diffEncoding.length != 0) {
+ diffEncoding.write(",");
+ }
+ diffEncoding.write(short);
+ }
+ nameCounter++;
+ }
+
+ // Startup code that loops over the method names and puts handlers on the
+ // Object class to catch noSuchMethod invocations.
+ ClassElement objectClass = compiler.objectClass;
+ String createInvocationMirror = namer.getName(
+ compiler.createInvocationMirrorElement);
+ String noSuchMethodName = namer.publicInstanceMethodNameByArity(
+ Compiler.NO_SUCH_METHOD, Compiler.NO_SUCH_METHOD_ARG_COUNT);
+ var type = 0;
+ if (useDiffEncoding) {
+ statements.addAll([
+ js['var objectClassObject = '
+ ' collectedClasses["${namer.getName(objectClass)}"],'
+ ' shortNames = "$diffEncoding".split(","),'
+ ' nameNumber = 0,'
+ ' diffEncodedString = shortNames[0],'
+ ' calculatedShortNames = [0, 1]'], // 0, 1 are args for splice.
+ js.for_('var i = 0', 'i < diffEncodedString.length', 'i++', [
+ js['var codes = [],'
+ ' diff = 0,'
+ ' digit = diffEncodedString.charCodeAt(i)'],
+ js.if_('digit == ${$PERIOD}', [
+ js['nameNumber = 0'],
+ js['digit = diffEncodedString.charCodeAt(++i)']
+ ]),
+ js.while_('digit <= ${$Z}', [
+ js['diff *= 26'],
+ js['diff += (digit - ${$A})'],
+ js['digit = diffEncodedString.charCodeAt(++i)']
+ ]),
+ js['diff *= 26'],
+ js['diff += (digit - ${$a})'],
+ js['nameNumber += diff'],
+ js.for_('var remaining = nameNumber',
+ 'remaining > 0',
+ 'remaining = ((remaining / 88) | 0)', [
+ js['codes.unshift(${$HASH} + (remaining % 88))']
+ ]),
+ js['calculatedShortNames.push('
+ ' String.fromCharCode.apply(String, codes))']
+ ]),
+ js['shortNames.splice.apply(shortNames, calculatedShortNames)']
+ ]);
+ } else {
+ // No useDiffEncoding version.
+ Iterable<String> longs = trivialNsmHandlers.map((selector) =>
+ selector.invocationMirrorMemberName);
+ String longNamesConstant = minify ? "" :
+ ',longNames = "${longs.join(",")}".split(",")';
+ statements.add(
+ js['var objectClassObject = '
+ ' collectedClasses["${namer.getName(objectClass)}"],'
+ ' shortNames = "$diffEncoding".split(",")'
+ ' $longNamesConstant']);
+ }
+
+ String sliceOffset = '," + (j < $firstNormalSelector ? 1 : 0)';
+ if (firstNormalSelector == 0) sliceOffset = '"';
+ if (firstNormalSelector == shorts.length) sliceOffset = ', 1"';
+
+ String whatToPatch = nativeEmitter.handleNoSuchMethod ?
+ "Object.prototype" :
+ "objectClassObject";
ngeoffray 2013/03/13 15:03:27 What is objectClassObject?
erikcorry 2013/03/14 10:06:57 It's the JS object corresponding to the Dart Objec
+
+ statements.addAll([
+ js.for_('var j = 0', 'j < shortNames.length', 'j++', [
+ js['var type = 0'],
+ js['var short = shortNames[j]'],
+ js.if_('short[0] == "${namer.getterPrefix[0]}"', js['type = 1']),
+ js.if_('short[0] == "${namer.setterPrefix[0]}"', js['type = 2']),
+ js['$whatToPatch[short] = Function("'
+ 'return this.$noSuchMethodName('
+ 'this,'
+ '${namer.CURRENT_ISOLATE}.$createInvocationMirror(\'"'
+ ' + ${minify ? "shortNames" : "longNames"}[j]'
+ ' + "\',\'" + short + "\',"'
+ ' + type'
+ ' + ",Array.prototype.slice.call(arguments'
+ '$sliceOffset'
+ ' + "),[]))")']
+ ])
+ ]);
+ }
+
jsAst.Fun get finishClassesFunction {
// Class descriptions are collected in a JS object.
// 'finishClasses' takes all collected descriptions and sets up
@@ -346,11 +564,7 @@ class CodeEmitterTask extends CompilerTask {
// the object literal directly. For other engines we have to create a new
// object and copy over the members.
- // function(collectedClasses,
- // isolateProperties,
- // existingIsolateProperties) {
- return js.fun(['collectedClasses', 'isolateProperties',
- 'existingIsolateProperties'], [
+ List<jsAst.Node> statements = [
js['var pendingClasses = {}'],
js['var hasOwnProperty = Object.prototype.hasOwnProperty'],
@@ -358,7 +572,7 @@ class CodeEmitterTask extends CompilerTask {
// for (var cls in collectedClasses) {
js.forIn('cls', 'collectedClasses', [
// if (hasOwnProperty.call(collectedClasses, cls)) {
- js.if_(js['hasOwnProperty.call(collectedClasses, cls)'], [
+ js.if_('hasOwnProperty.call(collectedClasses, cls)', [
js['var desc = collectedClasses[cls]'],
/* The 'fields' are either a constructor function or a
@@ -370,7 +584,7 @@ class CodeEmitterTask extends CompilerTask {
// var fields = desc[""], supr;
js['var fields = desc[""], supr'],
- js.if_(js['typeof fields == "string"'], [
+ js.if_('typeof fields == "string"', [
js['var s = fields.split(";")'],
js['supr = s[0]'],
js['fields = s[1] == "" ? [] : s[1].split(",")'],
@@ -381,7 +595,7 @@ class CodeEmitterTask extends CompilerTask {
js['isolateProperties[cls] = defineClass(cls, fields, desc)'],
// if (supr) pendingClasses[cls] = supr;
- js.if_(js['supr'], js['pendingClasses[cls] = supr'])
+ js.if_('supr', js['pendingClasses[cls] = supr'])
])
]),
@@ -389,10 +603,19 @@ class CodeEmitterTask extends CompilerTask {
// function finishClass(cls) { ... }
buildFinishClass(),
+ ];
+ addTrivialNsmHandlers(statements);
+
+ statements.add(
// for (var cls in pendingClasses) finishClass(cls);
- js.forIn('cls', 'pendingClasses', js['finishClass']('cls'))
- ]);
+ js.forIn('cls', 'pendingClasses', js['finishClass(cls)'])
+ );
+ // function(collectedClasses,
+ // isolateProperties,
+ // existingIsolateProperties) {
+ return js.fun(['collectedClasses', 'isolateProperties',
+ 'existingIsolateProperties'], statements);
}
jsAst.FunctionDeclaration buildFinishClass() {
@@ -405,14 +628,18 @@ class CodeEmitterTask extends CompilerTask {
js['var hasOwnProperty = Object.prototype.hasOwnProperty'],
// if (hasOwnProperty.call(finishedClasses, cls)) return;
- js.if_(js['hasOwnProperty.call(finishedClasses, cls)'],
+ js.if_('hasOwnProperty.call(finishedClasses, cls)',
js.return_()),
js['finishedClasses[cls] = true'],
+
js['var superclass = pendingClasses[cls]'],
- /* The superclass is only false (empty string) for Dart's Object class. */
- js.if_(js['!superclass'], js.return_()),
+ // The superclass is only false (empty string) for Dart's Object class.
+ // The minifier together with noSuchMethod can put methods on the
+ // Object.prototype object, and they show through here, so we check that
+ // we have a string.
+ js.if_('!superclass || typeof superclass != "string"', js.return_()),
js['finishClass(superclass)'],
js['var constructor = isolateProperties[cls]'],
js['var superConstructor = isolateProperties[superclass]'],
@@ -446,10 +673,10 @@ class CodeEmitterTask extends CompilerTask {
js.forIn('member', 'prototype', [
/* Short version of: if (member == '') */
// if (!member) continue;
- js.if_(js['!member'], new jsAst.Continue(null)),
+ js.if_('!member', new jsAst.Continue(null)),
// if (hasOwnProperty.call(prototype, member)) {
- js.if_(js['hasOwnProperty.call(prototype, member)'], [
+ js.if_('hasOwnProperty.call(prototype, member)', [
js['newPrototype[member] = prototype[member]']
])
])
@@ -511,7 +738,7 @@ class CodeEmitterTask extends CompilerTask {
// for (var staticName in isolateProperties) {
js.forIn('staticName', 'isolateProperties', [
- js.if_(js['hasOwnProperty.call(isolateProperties, staticName)'], [
+ js.if_('hasOwnProperty.call(isolateProperties, staticName)', [
js['str += ("this." + staticName + "= properties." + staticName + '
'";\\n")']
])
@@ -558,7 +785,7 @@ class CodeEmitterTask extends CompilerTask {
// try {
js.try_([
- js.if_(js['result === sentinelUndefined'], [
+ js.if_('result === sentinelUndefined', [
js['$isolate[fieldName] = sentinelInProgress'],
// try {
@@ -570,15 +797,15 @@ class CodeEmitterTask extends CompilerTask {
// stack trace.
// if (result === sentinelUndefined) {
- js.if_(js['result === sentinelUndefined'], [
+ js.if_('result === sentinelUndefined', [
// if ($isolate[fieldName] === sentinelInProgress) {
- js.if_(js['$isolate[fieldName] === sentinelInProgress'], [
+ js.if_('$isolate[fieldName] === sentinelInProgress', [
js['$isolate[fieldName] = null'],
])
])
])
], /* else */ [
- js.if_(js['result === sentinelInProgress'],
+ js.if_('result === sentinelInProgress',
js['$cyclicThrow(staticName)']
)
]),
@@ -1156,7 +1383,7 @@ class CodeEmitterTask extends CompilerTask {
assert(!backend.isInterceptorClass(member));
String getterName = namer.getterNameFromAccessorName(accessorName);
builder.addProperty(getterName,
- js.fun([], js.return_(js['this'][fieldName])));
+ js.fun([], js.return_(js['this.$fieldName'])));
}
void generateSetter(Element member, String fieldName, String accessorName,
@@ -1346,6 +1573,10 @@ class CodeEmitterTask extends CompilerTask {
bool get getterAndSetterCanBeImplementedByFieldSpec => true;
+ /// If this is true then we can generate the noSuchMethod handlers at startup
+ /// time, instead of them being emitted as part of the Object class.
+ bool get generateTrivialNsmHandlers => true;
+
int _selectorRank(Selector selector) {
int arity = selector.argumentCount * 3;
if (selector.isGetter()) return arity + 2;
@@ -1967,6 +2198,29 @@ class CodeEmitterTask extends CompilerTask {
}
}
+ // Identify the noSuchMethod handlers that are so simple that we can
+ // generate them programatically.
+ bool isTrivialNsmHandler(
+ int type, List argNames, Selector selector, String internalName) {
+ if (!generateTrivialNsmHandlers) return false;
+ // Check for wrong calling convention.
ngeoffray 2013/03/13 15:03:27 wrong :-)? maybe change it to "interceptor"?
erikcorry 2013/03/14 10:06:57 Done.
+ if (backend.isInterceptedName(selector.name)) {
+ // We can handle the strange calling convention used by intercepted names
ngeoffray 2013/03/13 15:03:27 strange -> interceptor
erikcorry 2013/03/14 10:06:57 Removed word 'strange'.
+ // in the diff encoding, but we don't use that for non-minified code.
+ if (!compiler.enableMinification) return false;
ngeoffray 2013/03/13 15:03:27 Isn't this code only used by the minifier?
erikcorry 2013/03/14 10:06:57 no
+ String shortName = namer.invocationMirrorInternalName(selector);
+ if (shortName.length > MAX_MINIFIED_LENGTH_FOR_DIFF_ENCODING) {
+ return false;
+ }
+ }
+ // Check for named arguments.
+ if (argNames.length != 0) return false;
+ // Check for unexpected name (this doesn't really happen).
+ if (internalName.startsWith(namer.getterPrefix[0])) return type == 1;
+ if (internalName.startsWith(namer.setterPrefix[0])) return type == 2;
+ return type == 0;
+ }
+
void emitNoSuchMethodHandlers(DefineStubFunction defineStub) {
// Do not generate no such method handlers if there is no class.
if (compiler.codegenWorld.instantiatedClasses.isEmpty) return;
@@ -1981,42 +2235,7 @@ class CodeEmitterTask extends CompilerTask {
// Keep track of the JavaScript names we've already added so we
// do not introduce duplicates (bad for code size).
- Set<String> addedJsNames = new Set<String>();
-
- jsAst.Expression generateMethod(String jsName, Selector selector) {
- // Values match JSInvocationMirror in js-helper library.
- int type = selector.invocationMirrorKind;
- String methodName = selector.invocationMirrorMemberName;
- List<jsAst.Parameter> parameters = <jsAst.Parameter>[];
- CodeBuffer args = new CodeBuffer();
- for (int i = 0; i < selector.argumentCount; i++) {
- parameters.add(new jsAst.Parameter('\$$i'));
- }
-
- List<jsAst.Expression> argNames =
- selector.getOrderedNamedArguments().map((SourceString name) =>
- js.string(name.slowToString())).toList();
-
- String internalName = namer.invocationMirrorInternalName(selector);
-
- String createInvocationMirror = namer.getName(
- compiler.createInvocationMirrorElement);
-
- assert(backend.isInterceptedName(Compiler.NO_SUCH_METHOD));
- jsAst.Expression expression = js['this.$noSuchMethodName'](
- [js['this'],
- js[namer.CURRENT_ISOLATE][createInvocationMirror]([
- js.string(methodName),
- js.string(internalName),
- type,
- new jsAst.ArrayInitializer.from(
- parameters.map((param) => js[param.name]).toList()),
- new jsAst.ArrayInitializer.from(argNames)])]);
- parameters = backend.isInterceptedName(selector.name)
- ? ([new jsAst.Parameter('\$receiver')]..addAll(parameters))
- : parameters;
- return js.fun(parameters, js.return_(expression));
- }
+ Map<String, Selector> addedJsNames = new Map<String, Selector>();
void addNoSuchMethodHandlers(SourceString ignore, Set<Selector> selectors) {
// Cache the object class and type.
@@ -2095,17 +2314,66 @@ class CodeEmitterTask extends CompilerTask {
compiler.world.locateNoSuchMethodHolders(selector);
if (holders.every(hasMatchingMember)) continue;
String jsName = namer.invocationMirrorInternalName(selector);
- if (!addedJsNames.contains(jsName)) {
- jsAst.Expression method = generateMethod(jsName, selector);
- defineStub(jsName, method);
- addedJsNames.add(jsName);
- }
+ addedJsNames[jsName] = selector;
}
}
compiler.codegenWorld.invokedNames.forEach(addNoSuchMethodHandlers);
compiler.codegenWorld.invokedGetters.forEach(addNoSuchMethodHandlers);
compiler.codegenWorld.invokedSetters.forEach(addNoSuchMethodHandlers);
+
+ // Set flag used by generateMethod helper below. If we have very few
+ // handlers we use defineStub for them all, rather than try to generate them
+ // at runtime.
+ bool haveVeryFewNoSuchMemberHandlers =
+ (addedJsNames.length < VERY_FEW_NO_SUCH_METHOD_HANDLERS);
+
+ jsAst.Expression generateMethod(String jsName, Selector selector) {
+ // Values match JSInvocationMirror in js-helper library.
+ int type = selector.invocationMirrorKind;
+ List<jsAst.Parameter> parameters = <jsAst.Parameter>[];
+ CodeBuffer args = new CodeBuffer();
+ for (int i = 0; i < selector.argumentCount; i++) {
+ parameters.add(new jsAst.Parameter('\$$i'));
+ }
+
+ List<jsAst.Expression> argNames =
+ selector.getOrderedNamedArguments().map((SourceString name) =>
+ js.string(name.slowToString())).toList();
+
+ String methodName = selector.invocationMirrorMemberName;
+ String internalName = namer.invocationMirrorInternalName(selector);
+ if (!haveVeryFewNoSuchMemberHandlers &&
+ isTrivialNsmHandler(type, argNames, selector, internalName)) {
+ trivialNsmHandlers.add(selector);
+ return null;
+ }
+
+ String createInvocationMirror = namer.getName(
+ compiler.createInvocationMirrorElement);
+
+ assert(backend.isInterceptedName(Compiler.NO_SUCH_METHOD));
+ jsAst.Expression expression = js['this.$noSuchMethodName'](
+ [js['this'],
+ js[namer.CURRENT_ISOLATE][createInvocationMirror]([
+ js.string(compiler.enableMinification ?
+ internalName : methodName),
+ js.string(internalName),
+ type,
+ new jsAst.ArrayInitializer.from(
+ parameters.map((param) => js[param.name]).toList()),
+ new jsAst.ArrayInitializer.from(argNames)])]);
+ parameters = backend.isInterceptedName(selector.name)
+ ? ([new jsAst.Parameter('\$receiver')]..addAll(parameters))
+ : parameters;
+ return js.fun(parameters, js.return_(expression));
+ }
+
+ for (String jsName in addedJsNames.keys.toList()..sort()) {
+ Selector selector = addedJsNames[jsName];
+ jsAst.Expression method = generateMethod(jsName, selector);
+ if (method != null) defineStub(jsName, method);
+ }
}
String buildIsolateSetup(CodeBuffer buffer,
@@ -2164,7 +2432,6 @@ if (typeof document !== "undefined" && document.readyState !== "complete") {
return js.return_(js[namer.isolateAccess(cls)]['prototype']);
}
- jsAst.VariableUse receiver = js['receiver'];
/**
* Build a JavaScrit AST node for doing a type check on
* [cls]. [cls] must be an interceptor class.
@@ -2173,7 +2440,7 @@ if (typeof document !== "undefined" && document.readyState !== "complete") {
jsAst.Expression condition;
assert(backend.isInterceptorClass(cls));
if (cls == backend.jsBoolClass) {
- condition = receiver.typeof.equals(js.string('boolean'));
+ condition = js['(typeof receiver) == "boolean"'];
} else if (cls == backend.jsIntClass ||
cls == backend.jsDoubleClass ||
cls == backend.jsNumberClass) {
@@ -2182,13 +2449,13 @@ if (typeof document !== "undefined" && document.readyState !== "complete") {
cls == backend.jsMutableArrayClass ||
cls == backend.jsFixedArrayClass ||
cls == backend.jsExtendableArrayClass) {
- condition = receiver['constructor'].equals('Array');
+ condition = js['receiver.constructor == Array'];
} else if (cls == backend.jsStringClass) {
- condition = receiver.typeof.equals(js.string('string'));
+ condition = js['(typeof receiver) == "string"'];
} else if (cls == backend.jsNullClass) {
- condition = receiver.equals(new jsAst.LiteralNull());
+ condition = js['receiver == null'];
} else if (cls == backend.jsFunctionClass) {
- condition = receiver.typeof.equals(js.string('function'));
+ condition = js['(typeof receiver) == "function"'];
} else {
throw 'internal error';
}
@@ -2237,7 +2504,7 @@ if (typeof document !== "undefined" && document.readyState !== "complete") {
hasDouble ? backend.jsDoubleClass : backend.jsNumberClass);
if (hasInt) {
- jsAst.Expression isInt = js['Math']['floor'](receiver).equals(receiver);
+ jsAst.Expression isInt = js['Math.floor(receiver) == receiver'];
whenNumber = js.block([
js.if_(isInt, buildReturnInterceptor(backend.jsIntClass)),
returnNumberClass]);
@@ -2245,7 +2512,7 @@ if (typeof document !== "undefined" && document.readyState !== "complete") {
whenNumber = returnNumberClass;
}
block.statements.add(
- js.if_(receiver.typeof.equals(js.string('number')),
+ js.if_('(typeof receiver) == "number"',
whenNumber));
}
@@ -2258,7 +2525,7 @@ if (typeof document !== "undefined" && document.readyState !== "complete") {
// Returning "undefined" 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.equals(new jsAst.LiteralNull()),
+ block.statements.add(js.if_('receiver == null',
js.return_(js.undefined())));
}
if (hasFunction) {
@@ -2272,7 +2539,7 @@ if (typeof document !== "undefined" && document.readyState !== "complete") {
if (hasArray) {
block.statements.add(buildInterceptorCheck(backend.jsArrayClass));
}
- block.statements.add(js.return_(receiver));
+ block.statements.add(js.return_(js['receiver']));
buffer.write(jsAst.prettyPrint(
js[isolateProperties][key].assign(js.fun(['receiver'], block)),
@@ -2382,7 +2649,7 @@ if (typeof document !== "undefined" && document.readyState !== "complete") {
// }
// :].
List<jsAst.Statement> body = <jsAst.Statement>[];
- body.add(js.if_(js['receiver'].equals(new jsAst.LiteralNull()),
+ body.add(js.if_('receiver == null',
js.return_(js['a0'].equals(new jsAst.LiteralNull()))));
body.add(js.if_(
isNotObject('receiver'),

Powered by Google App Engine
This is Rietveld 408576698