Incremental compiler: Use addStubs for adding methods.

R=johnniwinther@google.com

Review URL: https://codereview.chromium.org//764533002

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@42042 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
ahe@google.com 2014-12-01 14:11:34 +00:00
parent b19abb6151
commit 590e8b06a2
5 changed files with 113 additions and 41 deletions

View file

@ -34,7 +34,8 @@ class ClassBuilder {
fields.add(field);
}
jsAst.ObjectInitializer toObjectInitializer() {
jsAst.ObjectInitializer toObjectInitializer(
{bool omitClassDescriptor: false}) {
StringBuffer buffer = new StringBuffer();
if (superName != null) {
buffer.write('$superName');
@ -52,11 +53,17 @@ class ClassBuilder {
classData =
new jsAst.ArrayInitializer.from([classData]..addAll(fieldMetadata));
}
var fieldsAndProperties =
[new jsAst.Property(js.string(namer.classDescriptorProperty),
classData)]
..addAll(properties);
List<jsAst.Property> fieldsAndProperties;
if (!omitClassDescriptor) {
fieldsAndProperties = <jsAst.Property>[];
fieldsAndProperties.add(
new jsAst.Property(
js.string(namer.classDescriptorProperty), classData));
fieldsAndProperties
..addAll(properties);
} else {
fieldsAndProperties = properties;
}
return new jsAst.ObjectInitializer(fieldsAndProperties, isOneLiner: false);
}
}

View file

@ -1452,8 +1452,12 @@ class OldEmitter implements Emitter {
'this.\$dart_unsafe_eval.patch = function(a) { eval(a) }$N');
String schemaChange =
jsAst.prettyPrint(buildSchemaChangeFunction(), compiler).getText();
String addMethod =
jsAst.prettyPrint(buildIncrementalAddMethod(), compiler).getText();
mainBuffer.add(
'this.\$dart_unsafe_eval.schemaChange$_=$_$schemaChange$N');
mainBuffer.add(
'this.\$dart_unsafe_eval.addMethod$_=$_$addMethod$N');
}
if (isProgramSplit) {
/// We collect all the global state of the, so it can be passed to the
@ -1682,6 +1686,57 @@ function(newConstructor, oldConstructor, superclass) {
}''');
}
/// Used by incremental compilation to patch up an object ([holder]) with a
/// new (or updated) method. [arrayOrFunction] is either the new method, or
/// an array containing the method (see
/// [ContainerBuilder.addMemberMethodFromInfo]). [name] is the name of the
/// new method. [isStatic] tells if method is static (or
/// top-level). [globalFunctionsAccess] is a reference to
/// [embeddedNames.GLOBAL_FUNCTIONS].
jsAst.Fun buildIncrementalAddMethod() {
return js(r'''
function(originalDescriptor, name, holder, isStatic, globalFunctionsAccess) {
var arrayOrFunction = originalDescriptor[name];
var method;
if (arrayOrFunction.constructor === Array) {
var existing = holder[name];
var array = arrayOrFunction;
var descriptor = Object.create(null);
this.addStubs(
descriptor, arrayOrFunction, name, isStatic, originalDescriptor, []);
method = descriptor[name];
for (var property in descriptor) {
if (!Object.prototype.hasOwnProperty.call(descriptor, property)) continue;
var stub = descriptor[property];
var existingStub = holder[property];
if (stub === method || !existingStub) {
// Not replacing an existing stub.
holder[property] = method;
continue;
}
if (!stub.$getterStub) {
var error = new Error("Unexpected stub.");
error.stub = stub;
throw error;
}
// Invoke the existing stub to obtain the tear-off closure.
existingStub = existingStub();
// A stub already exist. Update all its references to [existing] to
// [method].
for (var reference in existingStub) {
if (existingStub[reference] === existing) {
existingStub[reference] = method;
}
}
}
} else {
method = arrayOrFunction;
holder[name] = method;
}
if (isStatic) globalFunctionsAccess[name] = method;
}''');
}
/// Returns a map from OutputUnit to a hash of its content. The hash uniquely
/// identifies the code of the output-unit. It does not include
/// boilerplate JS code, like the sourcemap directives or the hash

View file

@ -280,15 +280,29 @@ jsAst.Expression getReflectionDataParser(String classesCollector,
mangledNamesAccess,
mangledGlobalNamesAccess]);
List<jsAst.Statement> incrementalSupport = <jsAst.Statement>[];
if (compiler.hasIncrementalSupport) {
incrementalSupport.add(
js.statement(
r'self.$dart_unsafe_eval.addStubs = addStubs;'));
}
return js('''
(function (reflectionData) {
"use strict";
#; // header
#; // processStatics
#; // addStubs
#; // tearOffCode
#; // init
})''', [header, processStatics, addStubs, tearOffCode, init]);
#header;
#processStatics;
#addStubs;
#tearOffCode;
#incrementalSupport;
#init;
})''', {
'header': header,
'processStatics': processStatics,
'incrementalSupport': incrementalSupport,
'addStubs': addStubs,
'tearOffCode': tearOffCode,
'init': init});
}

View file

@ -49,6 +49,7 @@ import 'package:compiler/src/js_emitter/js_emitter.dart' show
ClassBuilder,
ClassEmitter,
CodeEmitterTask,
ContainerBuilder,
MemberInfo,
computeMixinClass;
@ -638,39 +639,36 @@ class LibraryUpdater extends JsFeatures {
}
jsAst.Node computeMemberUpdateJs(Element element) {
MemberInfo info = emitter.oldEmitter.containerBuilder
.analyzeMemberMethod(element);
MemberInfo info = containerBuilder.analyzeMemberMethod(element);
if (info == null) {
compiler.internalError(element, '${element.runtimeType}');
}
ClassBuilder builder = new ClassBuilder(element, namer);
containerBuilder.addMemberMethodFromInfo(info, builder);
jsAst.Node partialDescriptor =
builder.toObjectInitializer(omitClassDescriptor: true);
String name = info.name;
jsAst.Node function = info.code;
List<jsAst.Statement> statements = <jsAst.Statement>[];
bool isStatic = !element.isInstanceMember;
/// Either a global object (non-instance members) or a prototype (instance
/// members).
jsAst.Node holder;
if (element.isInstanceMember) {
jsAst.Node elementAccess = namer.elementAccess(element.enclosingClass);
statements.add(
js.statement('#.prototype.# = f', [elementAccess, name]));
holder = js('#.prototype', namer.elementAccess(element.enclosingClass));
} else {
jsAst.Node elementAccess = namer.elementAccess(element);
jsAst.Expression globalFunctionsAccess =
emitter.generateEmbeddedGlobalAccess(embeddedNames.GLOBAL_FUNCTIONS);
statements.add(
js.statement(
'#.# = # = f',
[globalFunctionsAccess, name, elementAccess]));
if (info.canTearOff) {
String globalName = namer.globalObjectFor(element);
statements.add(
js.statement(
'#.#().# = f',
[globalName, info.tearOffName, callNameFor(element)]));
}
holder = js('#', namer.globalObjectFor(element));
}
// Create a scope by creating a new function. The updated function literal
// is passed as an argument to this function which ensures that temporary
// names in updateScope don't shadow global names.
jsAst.Fun updateScope = js('function (f) { # }', [statements]);
return js.statement('(#)(#)', [updateScope, function]);
jsAst.Expression globalFunctionsAccess =
emitter.generateEmbeddedGlobalAccess(embeddedNames.GLOBAL_FUNCTIONS);
return js.statement(
r'self.$dart_unsafe_eval.addMethod(#, #, #, #, #)',
[partialDescriptor, js.string(name), holder,
new jsAst.LiteralBool(isStatic), globalFunctionsAccess]);
}
String prettyPrintJs(jsAst.Node node) {
@ -1133,6 +1131,8 @@ abstract class JsFeatures {
Namer get namer => backend.namer;
CodeEmitterTask get emitter => backend.emitter;
ContainerBuilder get containerBuilder => emitter.oldEmitter.containerBuilder;
}
class EmitterHelper extends JsFeatures {

View file

@ -866,8 +866,6 @@ main() {
const <ProgramResult>[
const ProgramResult(
r"""
import 'dart:collection';
class C {
static m() {
print('v1');
@ -875,8 +873,6 @@ class C {
}
main() {
// TODO(ahe): The incremental compiler magter ikke static tear-off closures.
new HashMap(equals: identical);
try {
C.m();
} catch (e) {