Fix for issue 20992 - Allow sending static/top-level functions to other isolates which are spawned using the 'spawn function' functionality.

R=regis@google.com

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

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@44297 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
asiva@google.com 2015-03-06 18:50:19 +00:00
parent b2d9992127
commit cfb2df1020
8 changed files with 328 additions and 35 deletions

View file

@ -6084,6 +6084,21 @@ bool Function::IsImplicitClosureFunction() const {
}
bool Function::IsImplicitStaticClosureFunction(RawFunction* func) {
NoGCScope no_gc;
uint32_t kind_tag = func->ptr()->kind_tag_;
if (KindBits::decode(kind_tag) != RawFunction::kClosureFunction) {
return false;
}
if (!StaticBit::decode(kind_tag)) {
return false;
}
RawClosureData* data = reinterpret_cast<RawClosureData*>(func->ptr()->data_);
RawFunction* parent_function = data->ptr()->parent_function_;
return (parent_function->ptr()->data_ == reinterpret_cast<RawObject*>(func));
}
RawFunction* Function::New() {
ASSERT(Object::function_class() != Class::null());
RawObject* raw = Object::Allocate(Function::kClassId,
@ -6383,10 +6398,15 @@ void Function::BuildSignatureParameters(
RawInstance* Function::ImplicitStaticClosure() const {
if (implicit_static_closure() == Instance::null()) {
ObjectStore* object_store = Isolate::Current()->object_store();
const Context& context = Context::Handle(object_store->empty_context());
const Instance& closure =
Instance::Handle(Closure::New(*this, context, Heap::kOld));
Isolate* isolate = Isolate::Current();
ObjectStore* object_store = isolate->object_store();
const Context& context = Context::Handle(isolate,
object_store->empty_context());
Instance& closure =
Instance::Handle(isolate, Closure::New(*this, context, Heap::kOld));
const char* error_str = NULL;
closure ^= closure.CheckAndCanonicalize(&error_str);
ASSERT(!closure.IsNull());
set_implicit_static_closure(closure);
}
return implicit_static_closure();

View file

@ -2135,6 +2135,7 @@ class Function : public Object {
bool IsImplicitStaticClosureFunction() const {
return is_static() && IsImplicitClosureFunction();
}
bool static IsImplicitStaticClosureFunction(RawFunction* func);
// Returns true if this function represents an implicit instance closure
// function.
@ -2377,6 +2378,7 @@ FOR_EACH_FUNCTION_KIND_BIT(DEFINE_BIT)
FINAL_HEAP_OBJECT_IMPLEMENTATION(Function, Object);
friend class Class;
friend class SnapshotWriter;
friend class Parser; // For set_eval_script.
// RawFunction::VisitFunctionPointers accesses the private constructor of
// Function.
@ -7335,8 +7337,13 @@ class Closure : public AllStatic {
// Indicates this class cannot be extended by dart code.
return -kWordSize;
}
static RawFunction* GetFunction(RawObject* obj) {
return *(reinterpret_cast<RawFunction**>(
reinterpret_cast<intptr_t>(obj->ptr()) + function_offset()));
}
friend class Class;
friend class SnapshotWriter;
};

View file

@ -542,7 +542,9 @@ class RawObject {
friend class Array;
friend class ByteBuffer;
friend class Code;
friend class Closure;
friend class FreeListElement;
friend class Function;
friend class GCMarker;
friend class ExternalTypedData;
friend class ForwardList;
@ -566,6 +568,7 @@ class RawObject {
friend class TypedData;
friend class TypedDataView;
friend class WeakProperty; // StorePointer
friend class Instance; // StorePointer
DISALLOW_ALLOCATION();
DISALLOW_IMPLICIT_CONSTRUCTORS(RawObject);
@ -685,6 +688,8 @@ class RawPatchClass : public RawObject {
RawObject** to() {
return reinterpret_cast<RawObject**>(&ptr()->source_class_);
}
friend class Function;
};
@ -772,6 +777,8 @@ class RawClosureData : public RawObject {
RawObject** to() {
return reinterpret_cast<RawObject**>(&ptr()->closure_);
}
friend class Function;
};

View file

@ -237,6 +237,46 @@ RawClass* SnapshotReader::ReadClassId(intptr_t object_id) {
}
RawObject* SnapshotReader::ReadStaticImplicitClosure(intptr_t object_id,
intptr_t class_header) {
ASSERT(kind_ == Snapshot::kMessage);
// First create a function object and associate it with the specified
// 'object_id'.
Function& func = Function::ZoneHandle(isolate(), Function::null());
AddBackRef(object_id, &func, kIsDeserialized);
// Read the library/class/function information and lookup the function.
str_ ^= ReadObjectImpl();
library_ = Library::LookupLibrary(str_);
if (library_.IsNull() || !library_.Loaded()) {
SetReadException("Invalid Library object found in message.");
}
str_ ^= ReadObjectImpl();
if (str_.Equals(Symbols::TopLevel())) {
str_ ^= ReadObjectImpl();
func = library_.LookupFunctionAllowPrivate(str_);
} else {
cls_ = library_.LookupClassAllowPrivate(str_);
if (cls_.IsNull()) {
OS::Print("Name of class not found %s\n", str_.ToCString());
SetReadException("Invalid Class object found in message.");
}
cls_.EnsureIsFinalized(isolate());
str_ ^= ReadObjectImpl();
func = cls_.LookupFunctionAllowPrivate(str_);
}
if (func.IsNull()) {
SetReadException("Invalid function object found in message.");
}
func = func.ImplicitClosureFunction();
ASSERT(!func.IsNull());
// Return the associated implicit static closure.
return func.ImplicitStaticClosure();
}
RawObject* SnapshotReader::ReadObjectImpl() {
int64_t value = Read<int64_t>();
if ((value & kSmiTagMask) == kSmiTag) {
@ -312,7 +352,8 @@ RawObject* SnapshotReader::ReadObjectRef() {
// Since we are only reading an object reference, If it is an instance kind
// then we only need to figure out the class of the object and allocate an
// instance of it. The individual fields will be read later.
if (SerializedHeaderData::decode(class_header) == kInstanceObjectId) {
intptr_t header_id = SerializedHeaderData::decode(class_header);
if (header_id == kInstanceObjectId) {
Instance& result = Instance::ZoneHandle(isolate(), Instance::null());
AddBackRef(object_id, &result, kIsNotDeserialized);
@ -326,6 +367,12 @@ RawObject* SnapshotReader::ReadObjectRef() {
result ^= Object::Allocate(cls_.id(), instance_size, HEAP_SPACE(kind_));
}
return result.raw();
} else if (header_id == kStaticImplicitClosureObjectId) {
// We skip the tags that have been written as the implicit static
// closure is going to be created in this isolate or the canonical
// version already created in the isolate will be used.
ReadTags();
return ReadStaticImplicitClosure(object_id, class_header);
}
ASSERT((class_header & kSmiTagMask) != kSmiTag);
@ -937,7 +984,8 @@ RawObject* SnapshotReader::ReadInlinedObject(intptr_t object_id) {
// Read the class header information and lookup the class.
intptr_t class_header = Read<int32_t>();
intptr_t tags = ReadTags();
if (SerializedHeaderData::decode(class_header) == kInstanceObjectId) {
intptr_t header_id = SerializedHeaderData::decode(class_header);
if (header_id == kInstanceObjectId) {
// Object is regular dart instance.
Instance* result = reinterpret_cast<Instance*>(GetBackRef(object_id));
intptr_t instance_size = 0;
@ -1002,6 +1050,11 @@ RawObject* SnapshotReader::ReadInlinedObject(intptr_t object_id) {
ASSERT(!result->IsNull());
}
return result->raw();
} else if (header_id == kStaticImplicitClosureObjectId) {
// We do not use the tags as the implicit static closure
// is going to be created in this isolate or the canonical
// version already created in the isolate will be used.
return ReadStaticImplicitClosure(object_id, class_header);
}
ASSERT((class_header & kSmiTagMask) != kSmiTag);
intptr_t class_id = LookupInternalClass(class_header);
@ -1215,10 +1268,10 @@ void SnapshotWriter::WriteObjectRef(RawObject* raw) {
WriteInstanceRef(raw, cls);
return;
}
// Object is being referenced, add it to the forward ref list and mark
// it so that future references to this object in the snapshot will use
// this object id. Mark it as not having been serialized yet so that we
// will serialize the object when we go through the forward list.
// Add object to the forward ref list and mark it so that future references
// to this object in the snapshot will use this object id. Mark it as having
// been serialized so that we do not serialize the object when we go through
// the forward list.
forward_list_.MarkAndAddObject(raw, kIsSerialized);
switch (class_id) {
#define SNAPSHOT_WRITE(clazz) \
@ -1592,6 +1645,29 @@ void SnapshotWriter::WriteClassId(RawClass* cls) {
}
void SnapshotWriter::WriteStaticImplicitClosure(intptr_t object_id,
RawFunction* func,
intptr_t tags) {
// Write out the serialization header value for this object.
WriteInlinedObjectHeader(object_id);
// Indicate this is a static implicit closure object.
Write<int32_t>(SerializedHeaderData::encode(kStaticImplicitClosureObjectId));
// Write out the tags.
WriteTags(tags);
// Write out the library url, class name and signature function name.
RawClass* cls = GetFunctionOwner(func);
ASSERT(cls != Class::null());
RawLibrary* library = cls->ptr()->library_;
ASSERT(library != Library::null());
WriteObjectImpl(library->ptr()->url_);
WriteObjectImpl(cls->ptr()->name_);
WriteObjectImpl(func->ptr()->name_);
}
void SnapshotWriter::ArrayWriteTo(intptr_t object_id,
intptr_t array_kind,
intptr_t tags,
@ -1625,34 +1701,63 @@ void SnapshotWriter::ArrayWriteTo(intptr_t object_id,
}
void SnapshotWriter::CheckIfSerializable(RawClass* cls) {
RawFunction* SnapshotWriter::IsSerializableClosure(RawClass* cls,
RawObject* obj) {
if (Class::IsSignatureClass(cls)) {
// We do not allow closure objects in an isolate message.
Isolate* isolate = Isolate::Current();
HANDLESCOPE(isolate);
// 'obj' is a closure as its class is a signature class, extract
// the function object to check if this closure can be sent in an
// isolate message.
RawFunction* func = Closure::GetFunction(obj);
// We only allow closure of top level methods or static functions in a
// class to be sent in isolate messages.
if (can_send_any_object() &&
Function::IsImplicitStaticClosureFunction(func)) {
return func;
}
// Not a closure of a top level method or static function, throw an
// exception as we do not allow these objects to be serialized.
HANDLESCOPE(isolate());
const Class& clazz = Class::Handle(isolate(), cls);
const Function& errorFunc = Function::Handle(isolate(), func);
ASSERT(!errorFunc.IsNull());
// All other closures are errors.
const char* format = "Illegal argument in isolate message"
" : (object is a closure - %s %s)";
" : (object is a closure - %s %s)";
UnmarkAll(); // Unmark objects now as we are about to print stuff.
const Class& clazz = Class::Handle(isolate, cls);
const Function& func = Function::Handle(isolate,
clazz.signature_function());
ASSERT(!func.IsNull());
intptr_t len = OS::SNPrint(NULL, 0, format,
clazz.ToCString(), func.ToCString()) + 1;
char* chars = isolate->current_zone()->Alloc<char>(len);
OS::SNPrint(chars, len, format, clazz.ToCString(), func.ToCString());
clazz.ToCString(), errorFunc.ToCString()) + 1;
char* chars = isolate()->current_zone()->Alloc<char>(len);
OS::SNPrint(chars, len, format, clazz.ToCString(), errorFunc.ToCString());
SetWriteException(Exceptions::kArgument, chars);
}
return Function::null();
}
RawClass* SnapshotWriter::GetFunctionOwner(RawFunction* func) {
RawObject* owner = func->ptr()->owner_;
uword tags = GetObjectTags(owner);
intptr_t class_id = RawObject::ClassIdTag::decode(tags);
if (class_id == kClassCid) {
return reinterpret_cast<RawClass*>(owner);
}
ASSERT(class_id == kPatchClassCid);
return reinterpret_cast<RawPatchClass*>(owner)->ptr()->patched_class_;
}
void SnapshotWriter::CheckForNativeFields(RawClass* cls) {
if (cls->ptr()->num_native_fields_ != 0) {
// We do not allow objects with native fields in an isolate message.
Isolate* isolate = Isolate::Current();
HANDLESCOPE(Isolate::Current());
HANDLESCOPE(isolate());
const char* format = "Illegal argument in isolate message"
" : (object extends NativeWrapper - %s)";
UnmarkAll(); // Unmark objects now as we are about to print stuff.
const Class& clazz = Class::Handle(isolate, cls);
const Class& clazz = Class::Handle(isolate(), cls);
intptr_t len = OS::SNPrint(NULL, 0, format, clazz.ToCString()) + 1;
char* chars = isolate->current_zone()->Alloc<char>(len);
char* chars = isolate()->current_zone()->Alloc<char>(len);
OS::SNPrint(chars, len, format, clazz.ToCString());
SetWriteException(Exceptions::kArgument, chars);
}
@ -1673,11 +1778,20 @@ void SnapshotWriter::WriteInstance(intptr_t object_id,
RawObject* raw,
RawClass* cls,
intptr_t tags) {
// First check if object is a closure or has native fields.
CheckIfSerializable(cls);
// Check if the instance has native fields and throw an exception if it does.
CheckForNativeFields(cls);
// Check if object is a closure that is serializable, if the object is a
// closure that is not serializable this will throw an exception.
RawFunction* func = IsSerializableClosure(cls, raw);
if (func != Function::null()) {
WriteStaticImplicitClosure(object_id, func, tags);
return;
}
// Object is regular dart instance.
intptr_t next_field_offset =
intptr_t next_field_offset = Class::IsSignatureClass(cls) ?
Closure::InstanceSize() :
cls->ptr()->next_field_offset_in_words_ << kWordSizeLog2;
ASSERT(next_field_offset > 0);
@ -1713,8 +1827,25 @@ void SnapshotWriter::WriteInstance(intptr_t object_id,
void SnapshotWriter::WriteInstanceRef(RawObject* raw, RawClass* cls) {
// First check if object is a closure or has native fields.
CheckIfSerializable(cls);
// Check if the instance has native fields and throw an exception if it does.
CheckForNativeFields(cls);
// Check if object is a closure that is serializable, if the object is a
// closure that is not serializable this will throw an exception.
RawFunction* func = IsSerializableClosure(cls, raw);
if (func != Function::null()) {
// Add object to the forward ref list and mark it so that future references
// to this object in the snapshot will use this object id. Mark it as having
// been serialized so that we do not serialize the object when we go through
// the forward list.
forward_list_.MarkAndAddObject(raw, kIsSerialized);
uword tags = raw->ptr()->tags_;
ASSERT(SerializedHeaderTag::decode(tags) == kObjectId);
intptr_t object_id = SerializedHeaderData::decode(tags);
tags = forward_list_.NodeForObjectId(object_id)->tags();
WriteStaticImplicitClosure(object_id, func, tags);
return;
}
// Object is being referenced, add it to the forward ref list and mark
// it so that future references to this object in the snapshot will use

View file

@ -347,6 +347,7 @@ class SnapshotReader : public BaseReader {
RawObject* AllocateUninitialized(intptr_t class_id, intptr_t size);
RawClass* ReadClassId(intptr_t object_id);
RawObject* ReadStaticImplicitClosure(intptr_t object_id, intptr_t cls_header);
RawObject* ReadObjectImpl();
RawObject* ReadObjectImpl(intptr_t header);
RawObject* ReadObjectRef();
@ -611,6 +612,9 @@ class SnapshotWriter : public BaseWriter {
void WriteObjectRef(RawObject* raw);
void WriteClassId(RawClass* cls);
void WriteStaticImplicitClosure(intptr_t object_id,
RawFunction* func,
intptr_t tags);
void WriteObjectImpl(RawObject* raw);
void WriteInlinedObject(RawObject* raw);
void WriteForwardedObjects();
@ -620,7 +624,9 @@ class SnapshotWriter : public BaseWriter {
RawSmi* length,
RawTypeArguments* type_arguments,
RawObject* data[]);
void CheckIfSerializable(RawClass* cls);
RawFunction* IsSerializableClosure(RawClass* cls, RawObject* obj);
RawClass* GetFunctionOwner(RawFunction* func);
void CheckForNativeFields(RawClass* cls);
void SetWriteException(Exceptions::ExceptionType type, const char* msg);
void WriteInstance(intptr_t object_id,
RawObject* raw,

View file

@ -22,7 +22,7 @@ enum {
// Object id has been optimized away; reader should use next available id.
kOmittedObjectId,
kClassIdsOffset = kDoubleObject,
kClassIdsOffset = kOmittedObjectId,
// The class ids of predefined classes are included in this list
// at an offset of kClassIdsOffset.
@ -42,6 +42,7 @@ enum {
kArrayType,
kInstanceObjectId,
kStaticImplicitClosureObjectId,
kMaxPredefinedObjectIds,
kInvalidIndex = -1,
};

View file

@ -0,0 +1,118 @@
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import "dart:isolate";
import "dart:async";
import "package:expect/expect.dart";
import "package:async_helper/async_helper.dart";
void toplevel(port, message) { port.send("toplevel:$message"); }
Function createFuncToplevel() => (p, m) { p.send(m); };
class C {
Function initializer;
Function body;
C() : initializer = ((p, m) { throw "initializer"; }) {
body = (p, m) { throw "body"; };
}
static void staticFunc(port, message) { port.send("static:$message"); }
static Function createFuncStatic() => (p, m) { throw "static expr"; };
void instanceMethod(p, m) { throw "instanceMethod"; }
Function createFuncMember() => (p, m) { throw "instance expr"; };
void call(n, p) { throw "C"; }
}
class Callable {
void call(p, m) { p.send(["callable", m]); }
}
void main() {
asyncStart();
// top-level functions, static functions, closures, instance methods
// or function expressions are not sendable to an isolate spawned using
// spawnUri.
testUnsendable("toplevel", toplevel);
testUnsendable("static", C.staticFunc);
var c = new C();
testUnsendable("instance method", c.instanceMethod);
testUnsendable("static context expression", createFuncToplevel());
testUnsendable("static context expression", C.createFuncStatic());
testUnsendable("initializer context expression", c.initializer);
testUnsendable("constructor context expression", c.body);
testUnsendable("instance method context expression", c.createFuncMember());
testUnsendable("toplevel", toplevel.call);
testUnsendable("static", C.staticFunc.call);
testUnsendable("callable object", new Callable().call);
asyncEnd();
return;
}
// Create a receive port that expects exactly one message.
// Pass the message to `callback` and return the sendPort.
SendPort singleMessagePort(callback) {
var p;
p = new RawReceivePort((v) { p.close(); callback(v); });
return p.sendPort;
}
// A singleMessagePort that expects the message to be a specific value.
SendPort expectMessagePort(message) {
asyncStart();
return singleMessagePort((v) {
Expect.equals(message, v);
asyncEnd();
});
}
// Creates a new isolate and a pair of ports that expect a single message
// to be sent to the other isolate and back to the callback function.
Future<SendPort> echoPort(callback(value)) {
Completer completer = new Completer<SendPort>();
SendPort replyPort = singleMessagePort(callback);
RawReceivePort initPort;
initPort = new RawReceivePort((p) {
completer.complete(p);
initPort.close();
});
return Isolate.spawn(_echo, [replyPort, initPort.sendPort])
.then((isolate) => completer.future);
}
void _echo(msg) {
var replyPort = msg[0];
RawReceivePort requestPort;
requestPort = new RawReceivePort((msg) {
replyPort.send(msg);
requestPort.close(); // Single echo only.
});
msg[1].send(requestPort.sendPort);
}
// Creates other isolate that waits for a single message, `msg`, on the returned
// port, and executes it as `msg[0](msg[1],msg[2])` in the other isolate.
Future<SendPort> callPort() {
Completer completer = new Completer<SendPort>();
SendPort initPort = singleMessagePort(completer.complete);
return Isolate.spawn(_call, initPort)
.then((_) => completer.future);
}
void _call(initPort) {
initPort.send(singleMessagePort(callFunc));
}
void testUnsendable(name, func) {
asyncStart();
Isolate.spawnUri(Uri.parse("function_send_test.dart"), [], func)
.then((v) => throw "allowed spawn direct?", onError: (e,s){ asyncEnd(); });
asyncStart();
Isolate.spawnUri(Uri.parse("function_send_test.dart"), [], [func])
.then((v) => throw "allowed spawn wrapped?", onError: (e,s){ asyncEnd(); });
}
void callFunc(message) {
message[0](message[1], message[2]);
}

View file

@ -21,11 +21,9 @@ kill_self_test: Fail # Not implemented yet
handle_error_test: Fail # Not implemented yet
handle_error2_test: Fail # Not implemented yet
handle_error3_test: Fail # Not implemented yet
function_send_test: Fail # Not implemented yet
message3_test/constList_identical: RuntimeError # Issue 21816
message3_test/constMap: RuntimeError # Issue 21816
message3_test/fun: RuntimeError # Issue 21585
message3_test/constInstance: RuntimeError # Issue 21816
message3_test/byteBuffer: Crash # Issue 21818
message3_test/int32x4: Crash # Issue 21818
@ -77,6 +75,9 @@ spawn_uri_exported_main_test: RuntimeError # Issue 16549
issue_21398_parent_isolate_test: RuntimeError # Issue 16549
issue_21398_parent_isolate1_test: RuntimeError # Issue 16549
issue_21398_parent_isolate2_test: Skip # Not implemented yet
function_send_test: Fail # Not implemented yet
function_send1_test: Fail # Not implemented yet
message3_test/fun: RuntimeError # Issue 21585
[ $compiler == dart2js && ( $runtime == ff || $runtime == safari || $runtime == drt || $runtime == chrome || $runtime == chromeOnAndroid) ]
isolate_stress_test: Pass, Slow # Issue 10697
@ -112,6 +113,8 @@ simple_message_test/none: Fail, OK # Issue 13921 Dom isolates don't support spaw
spawn_uri_missing_from_isolate_test: RuntimeError # Issue 17649
spawn_uri_missing_test: SkipSlow # Times out.
isolate_current_test: Fail, OK # Issue 13921 Dom isolates don't support spawnFunction
function_send_test: Fail, OK # 13921 Dom isolates don't support spawnFunction
function_send1_test: Fail, OK # 13921 Dom isolates don't support spawnFunction
[ $compiler == dartanalyzer || $compiler == dart2analyzer ]
browser/typed_data_message_test: StaticWarning