mirror of
https://github.com/dart-lang/sdk
synced 2024-09-19 20:51:50 +00:00
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:
parent
c0b31f9bb0
commit
f216834af8
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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++.
|
||||
|
|
Loading…
Reference in a new issue