Index: dart/runtime/vm/object.cc |
=================================================================== |
--- dart/runtime/vm/object.cc (revision 31530) |
+++ dart/runtime/vm/object.cc (working copy) |
@@ -1808,6 +1808,128 @@ |
} |
+intptr_t Class::FindFunctionIndex(const Function& needle) const { |
+ Isolate* isolate = Isolate::Current(); |
+ if (EnsureIsFinalized(isolate) != Error::null()) { |
+ return -1; |
+ } |
+ ReusableHandleScope reused_handles(isolate); |
+ Array& funcs = reused_handles.ArrayHandle(); |
+ funcs ^= functions(); |
+ ASSERT(!funcs.IsNull()); |
+ Function& function = reused_handles.FunctionHandle(); |
+ const intptr_t len = funcs.Length(); |
+ for (intptr_t i = 0; i < len; i++) { |
+ function ^= funcs.At(i); |
+ if (function.raw() == needle.raw()) { |
+ return i; |
+ } |
+ } |
+ // No function found. |
+ return -1; |
+} |
+ |
+ |
+RawFunction* Class::FunctionFromIndex(intptr_t idx) const { |
+ const Array& funcs = Array::Handle(functions()); |
+ if ((idx < 0) || (idx >= funcs.Length())) { |
+ return Function::null(); |
+ } |
+ Function& func = Function::Handle(); |
+ func ^= funcs.At(idx); |
+ ASSERT(!func.IsNull()); |
+ return func.raw(); |
+} |
+ |
+ |
+RawFunction* Class::ImplicitClosureFunctionFromIndex(intptr_t idx) const { |
+ const Array& funcs = Array::Handle(functions()); |
+ if ((idx < 0) || (idx >= funcs.Length())) { |
+ return Function::null(); |
+ } |
+ Function& func = Function::Handle(); |
+ func ^= funcs.At(idx); |
+ ASSERT(!func.IsNull()); |
+ if (!func.HasImplicitClosureFunction()) { |
+ return Function::null(); |
+ } |
+ const Function& closure_func = |
+ Function::Handle(func.ImplicitClosureFunction()); |
+ ASSERT(!closure_func.IsNull()); |
+ return closure_func.raw(); |
+} |
+ |
+ |
+intptr_t Class::FindImplicitClosureFunctionIndex(const Function& needle) const { |
+ Isolate* isolate = Isolate::Current(); |
+ if (EnsureIsFinalized(isolate) != Error::null()) { |
+ return -1; |
+ } |
+ ReusableHandleScope reused_handles(isolate); |
+ Array& funcs = reused_handles.ArrayHandle(); |
+ funcs ^= functions(); |
+ ASSERT(!funcs.IsNull()); |
+ Function& function = reused_handles.FunctionHandle(); |
+ Function& implicit_closure = Function::Handle(); |
+ const intptr_t len = funcs.Length(); |
+ for (intptr_t i = 0; i < len; i++) { |
+ function ^= funcs.At(i); |
+ implicit_closure ^= function.implicit_closure_function(); |
+ if (implicit_closure.IsNull()) { |
+ // Skip non-implicit closure functions. |
+ continue; |
+ } |
+ if (needle.raw() == implicit_closure.raw()) { |
+ return i; |
+ } |
+ } |
+ // No function found. |
+ return -1; |
+} |
+ |
+ |
+ |
+intptr_t Class::FindInvocationDispatcherFunctionIndex( |
+ const Function& needle) const { |
+ Isolate* isolate = Isolate::Current(); |
+ if (EnsureIsFinalized(isolate) != Error::null()) { |
+ return -1; |
+ } |
+ ReusableHandleScope reused_handles(isolate); |
+ Array& funcs = reused_handles.ArrayHandle(); |
+ funcs ^= invocation_dispatcher_cache(); |
+ ASSERT(!funcs.IsNull()); |
+ Object& object = reused_handles.ObjectHandle(); |
+ const intptr_t len = funcs.Length(); |
+ for (intptr_t i = 0; i < len; i++) { |
+ object = funcs.At(i); |
+ // The invocation_dispatcher_cache is a table with some entries that |
+ // are functions. |
+ if (object.IsFunction()) { |
+ if (Function::Cast(object).raw() == needle.raw()) { |
+ return i; |
+ } |
+ } |
+ } |
+ // No function found. |
+ return -1; |
+} |
+ |
+ |
+ |
+RawFunction* Class::InvocationDispatcherFunctionFromIndex(intptr_t idx) const { |
+ ReusableHandleScope reused_handles(Isolate::Current()); |
+ Array& dispatcher_cache = reused_handles.ArrayHandle(); |
+ dispatcher_cache ^= invocation_dispatcher_cache(); |
+ Object& object = reused_handles.ObjectHandle(); |
+ object = dispatcher_cache.At(idx); |
+ if (!object.IsFunction()) { |
+ return Function::null(); |
+ } |
+ return Function::Cast(object).raw(); |
+} |
+ |
+ |
void Class::AddClosureFunction(const Function& function) const { |
GrowableObjectArray& closures = |
GrowableObjectArray::Handle(raw_ptr()->closure_functions_); |
@@ -1848,7 +1970,40 @@ |
return closure.raw(); |
} |
+intptr_t Class::FindClosureIndex(const Function& needle) const { |
+ if (closures() == GrowableObjectArray::null()) { |
+ return -1; |
+ } |
+ Isolate* isolate = Isolate::Current(); |
+ ReusableHandleScope reused_handles(isolate); |
+ const GrowableObjectArray& closures_array = |
+ GrowableObjectArray::Handle(isolate, closures()); |
+ Function& closure = reused_handles.FunctionHandle(); |
+ intptr_t num_closures = closures_array.Length(); |
+ for (intptr_t i = 0; i < num_closures; i++) { |
+ closure ^= closures_array.At(i); |
+ ASSERT(!closure.IsNull()); |
+ if (closure.raw() == needle.raw()) { |
+ return i; |
+ } |
+ } |
+ return -1; |
+} |
+ |
+RawFunction* Class::ClosureFunctionFromIndex(intptr_t idx) const { |
+ const GrowableObjectArray& closures_array = |
+ GrowableObjectArray::Handle(closures()); |
+ if ((idx < 0) || (idx >= closures_array.Length())) { |
+ return Function::null(); |
+ } |
+ Function& func = Function::Handle(); |
+ func ^= closures_array.At(idx); |
+ ASSERT(!func.IsNull()); |
+ return func.raw(); |
+} |
+ |
+ |
void Class::set_signature_function(const Function& value) const { |
ASSERT(value.IsClosureFunction() || value.IsSignatureFunction()); |
StorePointer(&raw_ptr()->signature_function_, value.raw()); |
@@ -1985,6 +2140,7 @@ |
break; |
} |
sup_type = cls.super_type(); |
+ ClassFinalizer::ResolveType(cls, sup_type); |
cls = sup_type.type_class(); |
} while (true); |
set_num_type_arguments(num_type_args); |
@@ -2399,6 +2555,44 @@ |
} |
+intptr_t Class::FindFieldIndex(const Field& needle) const { |
+ Isolate* isolate = Isolate::Current(); |
+ if (EnsureIsFinalized(isolate) != Error::null()) { |
+ return -1; |
+ } |
+ ReusableHandleScope reused_handles(isolate); |
+ Array& fields_array = reused_handles.ArrayHandle(); |
+ fields_array ^= fields(); |
+ ASSERT(!fields_array.IsNull()); |
+ Field& field = reused_handles.FieldHandle(); |
+ String& field_name = reused_handles.StringHandle(); |
+ String& needle_name = String::Handle(isolate); |
+ needle_name ^= needle.name(); |
+ const intptr_t len = fields_array.Length(); |
+ for (intptr_t i = 0; i < len; i++) { |
+ field ^= fields_array.At(i); |
+ field_name ^= field.name(); |
+ if (field_name.Equals(needle_name)) { |
+ return i; |
+ } |
+ } |
+ // No field found. |
+ return -1; |
+} |
+ |
+ |
+RawField* Class::FieldFromIndex(intptr_t idx) const { |
+ const Array& flds = Array::Handle(fields()); |
+ if ((idx < 0) || (idx >= flds.Length())) { |
+ return Field::null(); |
+ } |
+ Field& field = Field::Handle(); |
+ field ^= flds.At(idx); |
+ ASSERT(!field.IsNull()); |
+ return field.raw(); |
+} |
+ |
+ |
template <class FakeInstance> |
RawClass* Class::New(intptr_t index) { |
ASSERT(Object::class_class() != Class::null()); |
@@ -3248,7 +3442,7 @@ |
const char* user_visible_class_name = |
String::Handle(UserVisibleName()).ToCString(); |
jsobj.AddProperty("type", JSONType(ref)); |
- jsobj.AddProperty("id", id()); |
+ jsobj.AddPropertyF("id", "classes/%" Pd "", id()); |
jsobj.AddProperty("name", internal_class_name); |
jsobj.AddProperty("user_name", user_visible_class_name); |
if (!ref) { |
@@ -4011,6 +4205,19 @@ |
} |
+RawAbstractTypeArguments* InstantiatedTypeArguments::Canonicalize() const { |
+ const intptr_t num_types = Length(); |
+ const TypeArguments& type_args = TypeArguments::Handle( |
+ TypeArguments::New(num_types, Heap::kOld)); |
+ AbstractType& type = AbstractType::Handle(); |
+ for (intptr_t i = 0; i < num_types; i++) { |
+ type = TypeAt(i); |
+ type_args.SetTypeAt(i, type); |
+ } |
+ return type_args.Canonicalize(); |
+} |
+ |
+ |
void InstantiatedTypeArguments::set_uninstantiated_type_arguments( |
const AbstractTypeArguments& value) const { |
StorePointer(&raw_ptr()->uninstantiated_type_arguments_, value.raw()); |
@@ -4360,6 +4567,54 @@ |
} |
+const char* Function::KindToCString(RawFunction::Kind kind) { |
+ switch (kind) { |
+ case RawFunction::kRegularFunction: |
+ return "kRegularFunction"; |
+ break; |
+ case RawFunction::kClosureFunction: |
+ return "kClosureFunction"; |
+ break; |
+ case RawFunction::kSignatureFunction: |
+ return "kSignatureFunction"; |
+ break; |
+ case RawFunction::kGetterFunction: |
+ return "kGetterFunction"; |
+ break; |
+ case RawFunction::kSetterFunction: |
+ return "kSetterFunction"; |
+ break; |
+ case RawFunction::kConstructor: |
+ return "kConstructor"; |
+ break; |
+ case RawFunction::kImplicitGetter: |
+ return "kImplicitGetter"; |
+ break; |
+ case RawFunction::kImplicitSetter: |
+ return "kImplicitSetter"; |
+ break; |
+ case RawFunction::kImplicitStaticFinalGetter: |
+ return "kImplicitStaticFinalGetter"; |
+ break; |
+ case RawFunction::kStaticInitializer: |
+ return "kStaticInitializer"; |
+ break; |
+ case RawFunction::kMethodExtractor: |
+ return "kMethodExtractor"; |
+ break; |
+ case RawFunction::kNoSuchMethodDispatcher: |
+ return "kNoSuchMethodDispatcher"; |
+ break; |
+ case RawFunction::kInvokeFieldDispatcher: |
+ return "kInvokeFieldDispatcher"; |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ return NULL; |
+ } |
+} |
+ |
+ |
void Function::SetRedirectionType(const Type& type) const { |
ASSERT(IsFactory()); |
Object& obj = Object::Handle(raw_ptr()->data_); |
@@ -5619,11 +5874,31 @@ |
const char* internal_function_name = String::Handle(name()).ToCString(); |
const char* function_name = |
String::Handle(QualifiedUserVisibleName()).ToCString(); |
- ObjectIdRing* ring = Isolate::Current()->object_id_ring(); |
- intptr_t id = ring->GetIdForObject(raw()); |
+ Class& cls = Class::Handle(Owner()); |
+ ASSERT(!cls.IsNull()); |
+ Error& err = Error::Handle(); |
+ err ^= cls.EnsureIsFinalized(Isolate::Current()); |
+ ASSERT(err.IsNull()); |
+ intptr_t id = -1; |
+ const char* selector = NULL; |
+ if (IsNonImplicitClosureFunction()) { |
+ id = cls.FindClosureIndex(*this); |
+ selector = "closures"; |
+ } else if (IsImplicitClosureFunction()) { |
+ id = cls.FindImplicitClosureFunctionIndex(*this); |
+ selector = "implicit_closures"; |
+ } else if (IsNoSuchMethodDispatcher() || IsInvokeFieldDispatcher()) { |
+ id = cls.FindInvocationDispatcherFunctionIndex(*this); |
+ selector = "dispatchers"; |
+ } else { |
+ id = cls.FindFunctionIndex(*this); |
+ selector = "functions"; |
+ } |
+ ASSERT(id >= 0); |
+ intptr_t cid = cls.id(); |
JSONObject jsobj(stream); |
jsobj.AddProperty("type", JSONType(ref)); |
- jsobj.AddProperty("id", id); |
+ jsobj.AddPropertyF("id", "classes/%" Pd "/%s/%" Pd "", cid, selector, id); |
jsobj.AddProperty("name", internal_function_name); |
jsobj.AddProperty("user_name", function_name); |
if (ref) return; |
@@ -5631,44 +5906,7 @@ |
jsobj.AddProperty("is_const", is_const()); |
jsobj.AddProperty("is_optimizable", is_optimizable()); |
jsobj.AddProperty("is_inlinable", IsInlineable()); |
- const char* kind_string = NULL; |
- switch (kind()) { |
- case RawFunction::kRegularFunction: |
- kind_string = "regular"; |
- break; |
- case RawFunction::kGetterFunction: |
- kind_string = "getter"; |
- break; |
- case RawFunction::kSetterFunction: |
- kind_string = "setter"; |
- break; |
- case RawFunction::kImplicitGetter: |
- kind_string = "implicit getter"; |
- break; |
- case RawFunction::kImplicitSetter: |
- kind_string = "implicit setter"; |
- break; |
- case RawFunction::kMethodExtractor: |
- kind_string = "method extractor"; |
- break; |
- case RawFunction::kNoSuchMethodDispatcher: |
- kind_string = "no such method"; |
- break; |
- case RawFunction::kClosureFunction: |
- kind_string = "closure"; |
- break; |
- case RawFunction::kConstructor: |
- kind_string = "constructor"; |
- break; |
- case RawFunction::kStaticInitializer: |
- kind_string = "static initializer"; |
- break; |
- case RawFunction::kImplicitStaticFinalGetter: |
- kind_string = "static final getter"; |
- break; |
- default: |
- UNREACHABLE(); |
- } |
+ const char* kind_string = Function::KindToCString(kind()); |
jsobj.AddProperty("kind", kind_string); |
jsobj.AddProperty("unoptimized_code", Object::Handle(unoptimized_code())); |
jsobj.AddProperty("usage_counter", usage_counter()); |
@@ -5952,10 +6190,12 @@ |
JSONObject jsobj(stream); |
const char* internal_field_name = String::Handle(name()).ToCString(); |
const char* field_name = String::Handle(UserVisibleName()).ToCString(); |
- ObjectIdRing* ring = Isolate::Current()->object_id_ring(); |
- intptr_t id = ring->GetIdForObject(raw()); |
+ Class& cls = Class::Handle(owner()); |
+ intptr_t id = cls.FindFieldIndex(*this); |
+ ASSERT(id >= 0); |
+ intptr_t cid = cls.id(); |
jsobj.AddProperty("type", JSONType(ref)); |
- jsobj.AddProperty("id", id); |
+ jsobj.AddPropertyF("id", "classes/%" Pd "/fields/%" Pd "", cid, id); |
jsobj.AddProperty("name", internal_field_name); |
jsobj.AddProperty("user_name", field_name); |
if (is_static()) { |
@@ -5965,7 +6205,7 @@ |
const Object& valueObj = Object::Handle(instance.GetField(*this)); |
jsobj.AddProperty("value", valueObj); |
} |
- Class& cls = Class::Handle(owner()); |
+ |
jsobj.AddProperty("owner", cls); |
AbstractType& declared_type = AbstractType::Handle(type()); |
cls = declared_type.type_class(); |
@@ -7119,12 +7359,14 @@ |
void Script::PrintToJSONStream(JSONStream* stream, bool ref) const { |
JSONObject jsobj(stream); |
- ObjectIdRing* ring = Isolate::Current()->object_id_ring(); |
- intptr_t id = ring->GetIdForObject(raw()); |
jsobj.AddProperty("type", JSONType(ref)); |
- jsobj.AddProperty("id", id); |
const String& name = String::Handle(url()); |
+ ASSERT(!name.IsNull()); |
+ const String& encoded_url = String::Handle(String::EncodeURI(name)); |
+ ASSERT(!encoded_url.IsNull()); |
+ jsobj.AddPropertyF("id", "scripts/%s", encoded_url.ToCString()); |
jsobj.AddProperty("name", name.ToCString()); |
+ jsobj.AddProperty("user_name", name.ToCString()); |
jsobj.AddProperty("kind", GetKindAsCString()); |
if (ref) { |
return; |
@@ -8193,13 +8435,13 @@ |
void Library::PrintToJSONStream(JSONStream* stream, bool ref) const { |
const char* library_name = String::Handle(name()).ToCString(); |
const char* library_url = String::Handle(url()).ToCString(); |
- ObjectIdRing* ring = Isolate::Current()->object_id_ring(); |
- intptr_t id = ring->GetIdForObject(raw()); |
+ intptr_t id = index(); |
+ ASSERT(id >= 0); |
JSONObject jsobj(stream); |
jsobj.AddProperty("type", JSONType(ref)); |
- jsobj.AddProperty("id", id); |
+ jsobj.AddPropertyF("id", "libraries/%" Pd "", id); |
jsobj.AddProperty("name", library_name); |
- jsobj.AddProperty("url", library_url); |
+ jsobj.AddProperty("user_name", library_url); |
if (ref) return; |
{ |
JSONArray jsarr(&jsobj, "classes"); |
@@ -9581,10 +9823,26 @@ |
} |
-void Code::Disassemble() const { |
+void Code::Disassemble(DisassemblyFormatter* formatter) const { |
+ const bool fix_patch = CodePatcher::CodeIsPatchable(*this) && |
+ CodePatcher::IsEntryPatched(*this); |
+ if (fix_patch) { |
+ // The disassembler may choke on illegal instructions if the code has been |
+ // patched, un-patch the code before disassembling and re-patch after. |
+ CodePatcher::RestoreEntry(*this); |
+ } |
const Instructions& instr = Instructions::Handle(instructions()); |
uword start = instr.EntryPoint(); |
- Disassembler::Disassemble(start, start + instr.size(), comments()); |
+ if (formatter == NULL) { |
+ Disassembler::Disassemble(start, start + instr.size(), comments()); |
+ } else { |
+ Disassembler::Disassemble(start, start + instr.size(), formatter, |
+ comments()); |
+ } |
+ if (fix_patch) { |
+ // Redo the patch. |
+ CodePatcher::PatchEntry(*this); |
+ } |
} |
@@ -9696,7 +9954,7 @@ |
// Check if object matches find condition. |
-bool Code::FindRawCodeVisitor::FindObject(RawObject* obj) { |
+bool Code::FindRawCodeVisitor::FindObject(RawObject* obj) const { |
return RawInstructions::ContainsPC(obj, pc_); |
} |
@@ -9766,25 +10024,33 @@ |
void Code::PrintToJSONStream(JSONStream* stream, bool ref) const { |
- ObjectIdRing* ring = Isolate::Current()->object_id_ring(); |
- intptr_t id = ring->GetIdForObject(raw()); |
JSONObject jsobj(stream); |
+ jsobj.AddProperty("type", JSONType(ref)); |
+ jsobj.AddPropertyF("id", "code/%" Px "", EntryPoint()); |
+ jsobj.AddPropertyF("start", "%" Px "", EntryPoint()); |
+ jsobj.AddPropertyF("end", "%" Px "", EntryPoint() + Size()); |
+ Function& func = Function::Handle(); |
+ func ^= function(); |
+ ASSERT(!func.IsNull()); |
+ String& name = String::Handle(); |
+ ASSERT(!func.IsNull()); |
+ name ^= func.name(); |
+ const char* internal_function_name = name.ToCString(); |
+ jsobj.AddPropertyF("name", "%s%s", is_optimized() ? "*" : "", |
+ internal_function_name); |
+ name ^= func.QualifiedUserVisibleName(); |
+ const char* function_name = name.ToCString(); |
+ jsobj.AddPropertyF("user_name", "%s%s", is_optimized() ? "*" : "", |
+ function_name); |
if (ref) { |
- jsobj.AddProperty("type", "@Code"); |
- jsobj.AddProperty("id", id); |
return; |
} |
- jsobj.AddProperty("type", "Code"); |
- jsobj.AddProperty("id", id); |
jsobj.AddProperty("is_optimized", is_optimized()); |
jsobj.AddProperty("is_alive", is_alive()); |
jsobj.AddProperty("function", Object::Handle(function())); |
JSONArray jsarr(&jsobj, "disassembly"); |
DisassembleToJSONStream formatter(jsarr); |
- const Instructions& instr = Instructions::Handle(instructions()); |
- uword start = instr.EntryPoint(); |
- Disassembler::Disassemble(start, start + instr.size(), &formatter, |
- comments()); |
+ Disassemble(&formatter); |
} |
@@ -11477,7 +11743,7 @@ |
JSONObject jsobj(stream); |
jsobj.AddProperty("type", JSONType(ref)); |
- jsobj.AddProperty("id", id); |
+ jsobj.AddPropertyF("id", "objects/%" Pd "", id); |
Class& cls = Class::Handle(this->clazz()); |
jsobj.AddProperty("class", cls); |
@@ -12320,6 +12586,7 @@ |
} |
#endif |
ASSERT(IsOld()); |
+ ASSERT(type_args.IsNull() || type_args.IsOld()); |
SetCanonical(); |
return this->raw(); |
} |
@@ -14335,6 +14602,154 @@ |
} |
+static bool IsPercent(int32_t c) { |
+ return c == '%'; |
+} |
+ |
+ |
+static bool IsHexCharacter(int32_t c) { |
+ if (c >= '0' && c <= '9') { |
+ return true; |
+ } |
+ if (c >= 'A' && c <= 'F') { |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+ |
+static bool IsURISafeCharacter(int32_t c) { |
+ if ((c >= '0') && (c <= '9')) { |
+ return true; |
+ } |
+ if ((c >= 'a') && (c <= 'z')) { |
+ return true; |
+ } |
+ if ((c >= 'A') && (c <= 'Z')) { |
+ return true; |
+ } |
+ return (c == '-') || (c == '_') || (c == '.') || (c == '~'); |
+} |
+ |
+ |
+static int32_t GetHexCharacter(int32_t c) { |
+ ASSERT(c >= 0); |
+ ASSERT(c < 16); |
+ const char* hex = "0123456789ABCDEF"; |
+ return hex[c]; |
+} |
+ |
+ |
+static int32_t GetHexValue(int32_t c) { |
+ if (c >= '0' && c <= '9') { |
+ return c - '0'; |
+ } |
+ if (c >= 'A' && c <= 'F') { |
+ return c - 'A' + 10; |
+ } |
+ UNREACHABLE(); |
+ return 0; |
+} |
+ |
+ |
+static int32_t MergeHexCharacters(int32_t c1, int32_t c2) { |
+ return GetHexValue(c1) << 4 | GetHexValue(c2); |
+} |
+ |
+ |
+RawString* String::EncodeURI(const String& str) { |
+ // URI encoding is only specified for one byte strings. |
+ ASSERT(str.IsOneByteString() || str.IsExternalOneByteString()); |
+ intptr_t num_escapes = 0; |
+ intptr_t len = str.Length(); |
+ { |
+ CodePointIterator cpi(str); |
+ while (cpi.Next()) { |
+ int32_t code_point = cpi.Current(); |
+ if (!IsURISafeCharacter(code_point)) { |
+ num_escapes += 2; |
+ } |
+ } |
+ } |
+ const String& dststr = String::Handle( |
+ OneByteString::New(len + num_escapes, Heap::kNew)); |
+ { |
+ intptr_t index = 0; |
+ CodePointIterator cpi(str); |
+ while (cpi.Next()) { |
+ int32_t code_point = cpi.Current(); |
+ if (!IsURISafeCharacter(code_point)) { |
+ OneByteString::SetCharAt(dststr, index, '%'); |
+ OneByteString::SetCharAt(dststr, index + 1, |
+ GetHexCharacter(code_point >> 4)); |
+ OneByteString::SetCharAt(dststr, index + 2, |
+ GetHexCharacter(code_point & 0xF)); |
+ index += 3; |
+ } else { |
+ OneByteString::SetCharAt(dststr, index, code_point); |
+ index += 1; |
+ } |
+ } |
+ } |
+ return dststr.raw(); |
+} |
+ |
+ |
+RawString* String::DecodeURI(const String& str) { |
+ // URI encoding is only specified for one byte strings. |
+ ASSERT(str.IsOneByteString() || str.IsExternalOneByteString()); |
+ CodePointIterator cpi(str); |
+ intptr_t num_escapes = 0; |
+ intptr_t len = str.Length(); |
+ { |
+ CodePointIterator cpi(str); |
+ while (cpi.Next()) { |
+ int32_t code_point = cpi.Current(); |
+ if (IsPercent(code_point)) { |
+ // Verify that the two characters following the % are hex digits. |
+ if (!cpi.Next()) { |
+ return str.raw(); |
+ } |
+ int32_t code_point = cpi.Current(); |
+ if (!IsHexCharacter(code_point)) { |
+ return str.raw(); |
+ } |
+ if (!cpi.Next()) { |
+ return str.raw(); |
+ } |
+ code_point = cpi.Current(); |
+ if (!IsHexCharacter(code_point)) { |
+ return str.raw(); |
+ } |
+ num_escapes += 2; |
+ } |
+ } |
+ } |
+ ASSERT(len - num_escapes > 0); |
+ const String& dststr = String::Handle( |
+ OneByteString::New(len - num_escapes, Heap::kNew)); |
+ { |
+ intptr_t index = 0; |
+ CodePointIterator cpi(str); |
+ while (cpi.Next()) { |
+ int32_t code_point = cpi.Current(); |
+ if (IsPercent(code_point)) { |
+ cpi.Next(); |
+ int32_t ch1 = cpi.Current(); |
+ cpi.Next(); |
+ int32_t ch2 = cpi.Current(); |
+ int32_t merged = MergeHexCharacters(ch1, ch2); |
+ OneByteString::SetCharAt(dststr, index, merged); |
+ } else { |
+ OneByteString::SetCharAt(dststr, index, code_point); |
+ } |
+ index++; |
+ } |
+ } |
+ return dststr.raw(); |
+} |
+ |
+ |
RawString* String::NewFormatted(const char* format, ...) { |
va_list args; |
va_start(args, format); |