Expose set_source service command for functions

- Can change signatures
- Currently only works for unexecuted functions.

R=rmacnak@google.com

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

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@42124 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
johnmccutchan@google.com 2014-12-04 23:35:24 +00:00
parent c0b31f9bb0
commit f216834af8
6 changed files with 214 additions and 5 deletions

View file

@ -2125,6 +2125,19 @@ void Class::AddFunction(const Function& function) const {
}
void Class::RemoveFunction(const Function& function) const {
const Array& arr = Array::Handle(functions());
StorePointer(&raw_ptr()->functions_, Object::empty_array().raw());
Function& entry = Function::Handle();
for (intptr_t i = 0; i < arr.Length(); i++) {
entry ^= arr.At(i);
if (function.raw() != entry.raw()) {
AddFunction(entry);
}
}
}
intptr_t Class::FindFunctionIndex(const Function& needle) const {
Isolate* isolate = Isolate::Current();
if (EnsureIsFinalized(isolate) != Error::null()) {

View file

@ -1107,6 +1107,7 @@ class Class : public Object {
RawArray* functions() const { return raw_ptr()->functions_; }
void SetFunctions(const Array& value) const;
void AddFunction(const Function& function) const;
void RemoveFunction(const Function& function) const;
intptr_t FindFunctionIndex(const Function& function) const;
RawFunction* FunctionFromIndex(intptr_t idx) const;
intptr_t FindImplicitClosureFunctionIndex(const Function& needle) const;
@ -2274,6 +2275,7 @@ FOR_EACH_FUNCTION_KIND_BIT(DEFINE_BIT)
FINAL_HEAP_OBJECT_IMPLEMENTATION(Function, Object);
friend class Class;
friend class Parser; // For set_eval_script.
// RawFunction::VisitFunctionPointers accesses the private constructor of
// Function.
friend class RawFunction;

View file

@ -1106,6 +1106,42 @@ ParsedFunction* Parser::ParseStaticFieldInitializer(const Field& field) {
}
RawObject* Parser::ParseFunctionFromSource(const Class& owning_class,
const String& source) {
Isolate* isolate = Isolate::Current();
StackZone zone(isolate);
LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) {
const String& uri = String::Handle(Symbols::New("dynamically-added"));
const Script& script = Script::Handle(
Script::New(uri, source, RawScript::kSourceTag));
const Library& owning_library = Library::Handle(owning_class.library());
const String& private_key = String::Handle(owning_library.private_key());
script.Tokenize(private_key);
const intptr_t token_pos = 0;
Parser parser(script, owning_library, token_pos);
parser.is_top_level_ = true;
parser.set_current_class(owning_class);
const String& class_name = String::Handle(owning_class.Name());
ClassDesc members(owning_class, class_name, false, token_pos);
const intptr_t metadata_pos = parser.SkipMetadata();
parser.ParseClassMemberDefinition(&members, metadata_pos);
ASSERT(members.functions().Length() == 1);
const Function& func =
Function::ZoneHandle(Function::RawCast(members.functions().At(0)));
func.set_eval_script(script);
ParsedFunction* parsed_function = new ParsedFunction(isolate, func);
Parser::ParseFunction(parsed_function);
return func.raw();
} else {
const Error& error = Error::Handle(isolate->object_store()->sticky_error());
isolate->object_store()->clear_sticky_error();
return error.raw();
}
UNREACHABLE();
}
SequenceNode* Parser::ParseStaticFinalGetter(const Function& func) {
TRACE_PARSER("ParseStaticFinalGetter");
ParamList params;
@ -3301,7 +3337,7 @@ void Parser::ParseMethodOrConstructor(ClassDesc* members, MemberDesc* method) {
TRACE_PARSER("ParseMethodOrConstructor");
ASSERT(CurrentToken() == Token::kLPAREN || method->IsGetter());
ASSERT(method->type != NULL);
ASSERT(method->name_pos > 0);
ASSERT(is_top_level_ || method->name_pos > 0);
ASSERT(current_member_ == method);
if (method->has_var) {

View file

@ -221,6 +221,10 @@ class Parser : public ValueObject {
// given static field.
static ParsedFunction* ParseStaticFieldInitializer(const Field& field);
// Returns a RawFunction or RawError.
static RawObject* ParseFunctionFromSource(const Class& owning_class,
const String& source);
// Parse a function to retrieve parameter information that is not retained in
// the dart::Function object. Returns either an error if the parse fails
// (which could be the case for local functions), or a flat array of entries

View file

@ -22,6 +22,7 @@
#include "vm/object_graph.h"
#include "vm/object_id_ring.h"
#include "vm/object_store.h"
#include "vm/parser.h"
#include "vm/port.h"
#include "vm/profiler.h"
#include "vm/reusable_handles.h"
@ -1276,6 +1277,36 @@ static bool HandleClassesFunctionsCoverage(
}
static bool HandleFunctionSetSource(
Isolate* isolate, const Class& cls, const Function& func, JSONStream* js) {
if (js->LookupOption("source") == NULL) {
PrintError(js, "set_source expects a 'source' option\n");
return true;
}
const String& source =
String::Handle(String::New(js->LookupOption("source")));
const Object& result = Object::Handle(
Parser::ParseFunctionFromSource(cls, source));
if (result.IsError()) {
Error::Cast(result).PrintJSON(js, false);
return true;
}
if (!result.IsFunction()) {
PrintError(js, "source did not compile to a function.\n");
return true;
}
// Replace function.
cls.RemoveFunction(func);
cls.AddFunction(Function::Cast(result));
JSONObject jsobj(js);
jsobj.AddProperty("type", "Success");
jsobj.AddProperty("id", "");
return true;
}
static bool HandleClassesFunctions(Isolate* isolate, const Class& cls,
JSONStream* js) {
if (js->num_arguments() != 4 && js->num_arguments() != 5) {
@ -1298,11 +1329,13 @@ static bool HandleClassesFunctions(Isolate* isolate, const Class& cls,
func.PrintJSON(js, false);
return true;
} else {
const char* subcollection = js->GetArgument(4);
if (strcmp(subcollection, "coverage") == 0) {
const char* subcommand = js->GetArgument(4);
if (strcmp(subcommand, "coverage") == 0) {
return HandleClassesFunctionsCoverage(isolate, func, js);
} else if (strcmp(subcommand, "set_source") == 0) {
return HandleFunctionSetSource(isolate, cls, func, js);
} else {
PrintError(js, "Invalid sub collection %s", subcollection);
PrintError(js, "Invalid sub command %s", subcommand);
return true;
}
}

View file

@ -1074,7 +1074,7 @@ TEST_CASE(Service_Classes) {
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
ExpectSubstringF(handler.msg(),
"{\"type\":\"Error\",\"id\":\"\",\"message\":\"Invalid sub collection x\","
"{\"type\":\"Error\",\"id\":\"\",\"message\":\"Invalid sub command x\","
"\"request\":"
"{\"arguments\":[\"classes\",\"%" Pd "\",\"functions\",\"b\",\"x\"],"
"\"option_keys\":[],\"option_values\":[]}}", cid);
@ -1120,6 +1120,127 @@ TEST_CASE(Service_Classes) {
}
TEST_CASE(Service_SetSource) {
const char* kScript =
"var port;\n" // Set to our mock port by C++.
"\n"
"class A {\n"
" a() { return 1; }\n"
" b() { return 0; }\n"
" c(String f) { return f.length; }\n"
"}\n"
"main() {\n"
" var z = new A();\n"
" return z.a();\n"
"}\n"
"runB() {\n"
" var z = new A();\n"
" return z.b();\n"
"}\n"
"runC() {\n"
" var z = new A();\n"
" return z.c();\n"
"}\n";
Isolate* isolate = Isolate::Current();
Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL);
EXPECT_VALID(lib);
Library& vmlib = Library::Handle();
vmlib ^= Api::UnwrapHandle(lib);
EXPECT(!vmlib.IsNull());
Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL);
EXPECT_VALID(result);
const Class& class_a = Class::Handle(GetClass(vmlib, "A"));
EXPECT(!class_a.IsNull());
intptr_t cid = class_a.id();
// Build a mock message handler and wrap it in a dart port.
ServiceTestMessageHandler handler;
Dart_Port port_id = PortMap::CreatePort(&handler);
Dart_Handle port = Api::NewHandle(isolate, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
Array& service_msg = Array::Handle();
// Request the class A over the service.
service_msg = EvalF(lib, "[0, port, ['classes', '%" Pd "'], [], []]", cid);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("\"type\":\"Class\"", handler.msg());
ExpectSubstringF(handler.msg(),
"\"id\":\"classes\\/%" Pd "\",\"name\":\"A\",", cid);
ExpectSubstringF(handler.msg(), "\"allocationStats\":");
ExpectSubstringF(handler.msg(), "\"tokenPos\":");
ExpectSubstringF(handler.msg(), "\"endTokenPos\":");
// Request function 'b' from class A.
service_msg = EvalF(lib,
"[0, port, ['classes', '%" Pd "', 'functions', 'b'],"
"[], []]", cid);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("\"type\":\"Function\"", handler.msg());
ExpectSubstringF(handler.msg(),
"\"id\":\"classes\\/%" Pd "\\/functions\\/b\","
"\"name\":\"b\",", cid);
// Invalid set source of function 'b' from class A.
service_msg = EvalF(
lib,
"[0, port, ['classes', '%" Pd "', 'functions', 'b', 'set_source'],"
"[], []]", cid);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("\"type\":\"Error\"", handler.msg());
EXPECT_SUBSTRING("set_source expects a 'source' option", handler.msg());
// Set source (with syntax error) of function 'b' from class A.
service_msg = EvalF(
lib,
"[0, port, ['classes', '%" Pd "', 'functions', 'b', 'set_source'],"
"['source'], ['b() { return 4 }']]", cid);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("\"type\":\"Error\"", handler.msg());
// Set source of function 'b' from class A.
service_msg = EvalF(
lib,
"[0, port, ['classes', '%" Pd "', 'functions', 'b', 'set_source'],"
"['source'], ['b() { return 4; }']]", cid);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("Success", handler.msg());
// Run function 'b' see that it is executing replaced code.
result = Dart_Invoke(lib, NewString("runB"), 0, NULL);
EXPECT_VALID(result);
ASSERT(Dart_IsInteger(result));
int64_t r;
result = Dart_IntegerToInt64(result, &r);
EXPECT_VALID(result);
EXPECT_EQ(4, r);
// Set source of function 'c' from class A, changing its signature.
service_msg = EvalF(
lib,
"[0, port, ['classes', '%" Pd "', 'functions', 'c', 'set_source'],"
"['source'], ['c() { return 99; }']]", cid);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("Success", handler.msg());
// Run function 'c' see that it is executing replaced code.
result = Dart_Invoke(lib, NewString("runC"), 0, NULL);
EXPECT_VALID(result);
ASSERT(Dart_IsInteger(result));
result = Dart_IntegerToInt64(result, &r);
EXPECT_VALID(result);
EXPECT_EQ(99, r);
}
TEST_CASE(Service_Types) {
const char* kScript =
"var port;\n" // Set to our mock port by C++.