Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(42)

Side by Side Diff: src/objects.cc

Issue 1180073002: Introduce DefineOwnPropertyIgnoreAttributes and make it call SetPropertyWithInterceptor (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/objects.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 the V8 project authors. All rights reserved. 1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <iomanip> 5 #include <iomanip>
6 #include <sstream> 6 #include <sstream>
7 7
8 #include "src/v8.h" 8 #include "src/v8.h"
9 9
10 #include "src/accessors.h" 10 #include "src/accessors.h"
(...skipping 518 matching lines...) Expand 10 before | Expand all | Expand 10 after
529 if (accessors->IsAccessorInfo()) { 529 if (accessors->IsAccessorInfo()) {
530 if (AccessorInfo::cast(*accessors)->all_can_write()) return true; 530 if (AccessorInfo::cast(*accessors)->all_can_write()) return true;
531 } 531 }
532 } 532 }
533 } 533 }
534 return false; 534 return false;
535 } 535 }
536 536
537 537
538 MaybeHandle<Object> JSObject::SetPropertyWithFailedAccessCheck( 538 MaybeHandle<Object> JSObject::SetPropertyWithFailedAccessCheck(
539 LookupIterator* it, Handle<Object> value, LanguageMode language_mode) { 539 LookupIterator* it, Handle<Object> value) {
540 Handle<JSObject> checked = it->GetHolder<JSObject>(); 540 Handle<JSObject> checked = it->GetHolder<JSObject>();
541 if (FindAllCanWriteHolder(it)) { 541 if (FindAllCanWriteHolder(it)) {
542 return SetPropertyWithAccessor(it, value, language_mode); 542 // The supplied language-mode is ignored by SetPropertyWithAccessor.
543 return SetPropertyWithAccessor(it, value, SLOPPY);
543 } 544 }
544 545
545 it->isolate()->ReportFailedAccessCheck(checked); 546 it->isolate()->ReportFailedAccessCheck(checked);
546 RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(it->isolate(), Object); 547 RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(it->isolate(), Object);
547 return value; 548 return value;
548 } 549 }
549 550
550 551
551 void JSObject::SetNormalizedProperty(Handle<JSObject> object, 552 void JSObject::SetNormalizedProperty(Handle<JSObject> object,
552 Handle<Name> name, 553 Handle<Name> name,
(...skipping 2475 matching lines...) Expand 10 before | Expand all | Expand 10 after
3028 bool done = false; 3029 bool done = false;
3029 for (; it->IsFound(); it->Next()) { 3030 for (; it->IsFound(); it->Next()) {
3030 switch (it->state()) { 3031 switch (it->state()) {
3031 case LookupIterator::NOT_FOUND: 3032 case LookupIterator::NOT_FOUND:
3032 UNREACHABLE(); 3033 UNREACHABLE();
3033 3034
3034 case LookupIterator::ACCESS_CHECK: 3035 case LookupIterator::ACCESS_CHECK:
3035 if (it->HasAccess()) break; 3036 if (it->HasAccess()) break;
3036 // Check whether it makes sense to reuse the lookup iterator. Here it 3037 // Check whether it makes sense to reuse the lookup iterator. Here it
3037 // might still call into setters up the prototype chain. 3038 // might still call into setters up the prototype chain.
3038 return JSObject::SetPropertyWithFailedAccessCheck(it, value, 3039 return JSObject::SetPropertyWithFailedAccessCheck(it, value);
3039 language_mode);
3040 3040
3041 case LookupIterator::JSPROXY: 3041 case LookupIterator::JSPROXY:
3042 if (it->HolderIsReceiverOrHiddenPrototype()) { 3042 if (it->HolderIsReceiverOrHiddenPrototype()) {
3043 return JSProxy::SetPropertyWithHandler( 3043 return JSProxy::SetPropertyWithHandler(
3044 it->GetHolder<JSProxy>(), it->GetReceiver(), it->GetName(), value, 3044 it->GetHolder<JSProxy>(), it->GetReceiver(), it->GetName(), value,
3045 language_mode); 3045 language_mode);
3046 } else { 3046 } else {
3047 // TODO(verwaest): Use the MaybeHandle to indicate result. 3047 // TODO(verwaest): Use the MaybeHandle to indicate result.
3048 bool has_result = false; 3048 bool has_result = false;
3049 MaybeHandle<Object> maybe_result = 3049 MaybeHandle<Object> maybe_result =
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after
3149 3149
3150 LookupIterator::Configuration c = LookupIterator::OWN; 3150 LookupIterator::Configuration c = LookupIterator::OWN;
3151 LookupIterator own_lookup = 3151 LookupIterator own_lookup =
3152 it->IsElement() 3152 it->IsElement()
3153 ? LookupIterator(it->isolate(), it->GetReceiver(), it->index(), c) 3153 ? LookupIterator(it->isolate(), it->GetReceiver(), it->index(), c)
3154 : LookupIterator(it->GetReceiver(), it->name(), c); 3154 : LookupIterator(it->GetReceiver(), it->name(), c);
3155 3155
3156 for (; own_lookup.IsFound(); own_lookup.Next()) { 3156 for (; own_lookup.IsFound(); own_lookup.Next()) {
3157 switch (own_lookup.state()) { 3157 switch (own_lookup.state()) {
3158 case LookupIterator::ACCESS_CHECK: 3158 case LookupIterator::ACCESS_CHECK:
3159 if (!it->isolate()->MayAccess(own_lookup.GetHolder<JSObject>())) { 3159 if (!own_lookup.HasAccess()) {
3160 return JSObject::SetPropertyWithFailedAccessCheck(&own_lookup, value, 3160 return JSObject::SetPropertyWithFailedAccessCheck(&own_lookup, value);
3161 language_mode);
3162 } 3161 }
3163 break; 3162 break;
3164 3163
3165 case LookupIterator::INTEGER_INDEXED_EXOTIC: 3164 case LookupIterator::INTEGER_INDEXED_EXOTIC:
3166 return result; 3165 return result;
3167 3166
3168 case LookupIterator::DATA: { 3167 case LookupIterator::DATA: {
3169 PropertyDetails details = own_lookup.property_details(); 3168 PropertyDetails details = own_lookup.property_details();
3170 if (details.IsConfigurable() || !details.IsReadOnly()) { 3169 if (details.IsConfigurable() || !details.IsReadOnly()) {
3171 return JSObject::ReconfigureAsDataProperty(&own_lookup, value, 3170 return JSObject::DefineOwnPropertyIgnoreAttributes(
3172 details.attributes()); 3171 &own_lookup, value, details.attributes());
3173 } 3172 }
3174 return WriteToReadOnlyProperty(&own_lookup, value, language_mode); 3173 return WriteToReadOnlyProperty(&own_lookup, value, language_mode);
3175 } 3174 }
3176 3175
3177 case LookupIterator::ACCESSOR: { 3176 case LookupIterator::ACCESSOR: {
3178 PropertyDetails details = own_lookup.property_details(); 3177 PropertyDetails details = own_lookup.property_details();
3179 if (details.IsConfigurable()) { 3178 if (details.IsConfigurable()) {
3180 return JSObject::ReconfigureAsDataProperty(&own_lookup, value, 3179 return JSObject::DefineOwnPropertyIgnoreAttributes(
3181 details.attributes()); 3180 &own_lookup, value, details.attributes());
3182 } 3181 }
3183 3182
3184 return RedefineNonconfigurableProperty(it->isolate(), it->GetName(), 3183 return RedefineNonconfigurableProperty(it->isolate(), it->GetName(),
3185 value, language_mode); 3184 value, language_mode);
3186 } 3185 }
3187 3186
3188 case LookupIterator::INTERCEPTOR: 3187 case LookupIterator::INTERCEPTOR:
3189 case LookupIterator::JSPROXY: { 3188 case LookupIterator::JSPROXY: {
3190 bool found = false; 3189 bool found = false;
3191 MaybeHandle<Object> result = SetPropertyInternal( 3190 MaybeHandle<Object> result = SetPropertyInternal(
(...skipping 933 matching lines...) Expand 10 before | Expand all | Expand 10 after
4125 } 4124 }
4126 4125
4127 4126
4128 // static 4127 // static
4129 void ExecutableAccessorInfo::ClearSetter(Handle<ExecutableAccessorInfo> info) { 4128 void ExecutableAccessorInfo::ClearSetter(Handle<ExecutableAccessorInfo> info) {
4130 Handle<Object> object = v8::FromCData(info->GetIsolate(), nullptr); 4129 Handle<Object> object = v8::FromCData(info->GetIsolate(), nullptr);
4131 info->set_setter(*object); 4130 info->set_setter(*object);
4132 } 4131 }
4133 4132
4134 4133
4135 MaybeHandle<Object> JSObject::ReconfigureAsDataProperty( 4134 // Reconfigures a property to a data property with attributes, even if it is not
4135 // reconfigurable.
4136 // Requires a LookupIterator that does not look at the prototype chain beyond
4137 // hidden prototypes.
4138 MaybeHandle<Object> JSObject::DefineOwnPropertyIgnoreAttributes(
4136 LookupIterator* it, Handle<Object> value, PropertyAttributes attributes, 4139 LookupIterator* it, Handle<Object> value, PropertyAttributes attributes,
4137 ExecutableAccessorInfoHandling handling) { 4140 ExecutableAccessorInfoHandling handling) {
4138 Handle<JSObject> object = Handle<JSObject>::cast(it->GetReceiver()); 4141 Handle<JSObject> object = Handle<JSObject>::cast(it->GetReceiver());
4139 bool is_observed = object->map()->is_observed() && 4142 bool is_observed = object->map()->is_observed() &&
4140 (it->IsElement() || 4143 (it->IsElement() ||
4141 !it->isolate()->IsInternallyUsedPropertyName(it->name())); 4144 !it->isolate()->IsInternallyUsedPropertyName(it->name()));
4142 4145
4143 switch (it->state()) { 4146 for (; it->IsFound(); it->Next()) {
4144 case LookupIterator::INTERCEPTOR: 4147 switch (it->state()) {
4145 case LookupIterator::JSPROXY: 4148 case LookupIterator::JSPROXY:
4146 case LookupIterator::NOT_FOUND: 4149 case LookupIterator::NOT_FOUND:
4147 case LookupIterator::TRANSITION: 4150 case LookupIterator::TRANSITION:
4148 case LookupIterator::ACCESS_CHECK: 4151 UNREACHABLE();
4149 UNREACHABLE();
4150 4152
4151 case LookupIterator::INTEGER_INDEXED_EXOTIC: 4153 case LookupIterator::ACCESS_CHECK:
4152 return value; 4154 if (!it->HasAccess()) {
4155 return SetPropertyWithFailedAccessCheck(it, value);
4156 }
4157 break;
4153 4158
4154 case LookupIterator::ACCESSOR: { 4159 // If there's an interceptor, try to store the property with the
4155 PropertyDetails details = it->property_details(); 4160 // interceptor.
4156 // Ensure the context isn't changed after calling into accessors. 4161 // In case of success, the attributes will have been reset to the default
4157 AssertNoContextChange ncc(it->isolate()); 4162 // attributes of the interceptor, rather than the incoming attributes.
4163 //
4164 // TODO(verwaest): JSProxy afterwards verify the attributes that the
4165 // JSProxy claims it has, and verifies that they are compatible. If not,
4166 // they throw. Here we should do the same.
4167 case LookupIterator::INTERCEPTOR:
4168 if (handling == DONT_FORCE_FIELD) {
4169 MaybeHandle<Object> maybe_result =
4170 JSObject::SetPropertyWithInterceptor(it, value);
4171 if (!maybe_result.is_null()) return maybe_result;
4172 if (it->isolate()->has_pending_exception()) return maybe_result;
4173 }
4174 break;
4158 4175
4159 Handle<Object> accessors = it->GetAccessors(); 4176 case LookupIterator::INTEGER_INDEXED_EXOTIC:
4177 return value;
4160 4178
4161 // Special handling for ExecutableAccessorInfo, which behaves like a 4179 case LookupIterator::ACCESSOR: {
4162 // data property. 4180 Handle<Object> accessors = it->GetAccessors();
4163 if (accessors->IsExecutableAccessorInfo() &&
4164 handling == DONT_FORCE_FIELD) {
4165 Handle<Object> result;
4166 ASSIGN_RETURN_ON_EXCEPTION(
4167 it->isolate(), result,
4168 JSObject::SetPropertyWithAccessor(it, value, STRICT), Object);
4169 DCHECK(result->SameValue(*value));
4170 4181
4171 if (details.attributes() == attributes) return value; 4182 // Special handling for ExecutableAccessorInfo, which behaves like a
4183 // data property.
4184 if (accessors->IsExecutableAccessorInfo() &&
4185 handling == DONT_FORCE_FIELD) {
4186 PropertyDetails details = it->property_details();
4187 // Ensure the context isn't changed after calling into accessors.
4188 AssertNoContextChange ncc(it->isolate());
4172 4189
4173 // Reconfigure the accessor if attributes mismatch. 4190 Handle<Object> result;
4174 Handle<ExecutableAccessorInfo> new_data = Accessors::CloneAccessor( 4191 ASSIGN_RETURN_ON_EXCEPTION(
4175 it->isolate(), Handle<ExecutableAccessorInfo>::cast(accessors)); 4192 it->isolate(), result,
4176 new_data->set_property_attributes(attributes); 4193 JSObject::SetPropertyWithAccessor(it, value, STRICT), Object);
4177 // By clearing the setter we don't have to introduce a lookup to 4194 DCHECK(result->SameValue(*value));
4178 // the setter, simply make it unavailable to reflect the 4195
4179 // attributes. 4196 if (details.attributes() == attributes) return value;
4180 if (attributes & READ_ONLY) { 4197
4181 ExecutableAccessorInfo::ClearSetter(new_data); 4198 // Reconfigure the accessor if attributes mismatch.
4199 Handle<ExecutableAccessorInfo> new_data = Accessors::CloneAccessor(
4200 it->isolate(), Handle<ExecutableAccessorInfo>::cast(accessors));
4201 new_data->set_property_attributes(attributes);
4202 // By clearing the setter we don't have to introduce a lookup to
4203 // the setter, simply make it unavailable to reflect the
4204 // attributes.
4205 if (attributes & READ_ONLY) {
4206 ExecutableAccessorInfo::ClearSetter(new_data);
4207 }
4208
4209 if (it->IsElement()) {
4210 SetElementCallback(it->GetHolder<JSObject>(), it->index(), new_data,
4211 attributes);
4212 } else {
4213 SetPropertyCallback(it->GetHolder<JSObject>(), it->name(), new_data,
4214 attributes);
4215 }
4216 if (is_observed) {
Igor Sheludko 2015/06/12 12:53:08 Please pull "if (is_observed)" part out of both br
4217 RETURN_ON_EXCEPTION(
4218 it->isolate(),
4219 EnqueueChangeRecord(object, "reconfigure", it->GetName(),
4220 it->factory()->the_hole_value()),
4221 Object);
4222 }
4223 } else {
4224 it->ReconfigureDataProperty(value, attributes);
4225 it->WriteDataValue(value);
4226
4227 if (is_observed) {
4228 RETURN_ON_EXCEPTION(
4229 it->isolate(),
4230 EnqueueChangeRecord(object, "reconfigure", it->GetName(),
4231 it->factory()->the_hole_value()),
4232 Object);
4233 }
4182 } 4234 }
4183 4235
4184 if (it->IsElement()) { 4236 return value;
4185 SetElementCallback(it->GetHolder<JSObject>(), it->index(), new_data, 4237 }
4186 attributes); 4238 case LookupIterator::DATA: {
4187 } else { 4239 PropertyDetails details = it->property_details();
4188 SetPropertyCallback(it->GetHolder<JSObject>(), it->name(), new_data, 4240 Handle<Object> old_value = it->factory()->the_hole_value();
4189 attributes); 4241 // Regular property update if the attributes match.
4242 if (details.attributes() == attributes) {
4243 return SetDataProperty(it, value);
4190 } 4244 }
4245
4246 // Special case: properties of typed arrays cannot be reconfigured to
4247 // non-writable nor to non-enumerable.
4248 if (it->IsElement() && (object->HasExternalArrayElements() ||
4249 object->HasFixedTypedArrayElements())) {
4250 return RedefineNonconfigurableProperty(it->isolate(), it->GetName(),
4251 value, STRICT);
4252 }
4253
4254 // Reconfigure the data property if the attributes mismatch.
4255 if (is_observed) old_value = it->GetDataValue();
4256
4257 it->ReconfigureDataProperty(value, attributes);
4258 it->WriteDataValue(value);
4259
4191 if (is_observed) { 4260 if (is_observed) {
4192 RETURN_ON_EXCEPTION( 4261 if (old_value->SameValue(*value)) {
4193 it->isolate(), 4262 old_value = it->factory()->the_hole_value();
4194 EnqueueChangeRecord(object, "reconfigure", it->GetName(), 4263 }
4195 it->factory()->the_hole_value()), 4264 RETURN_ON_EXCEPTION(it->isolate(),
4196 Object); 4265 EnqueueChangeRecord(object, "reconfigure",
4266 it->GetName(), old_value),
4267 Object);
4197 } 4268 }
4198 return value; 4269 return value;
4199 } 4270 }
4200
4201 it->ReconfigureDataProperty(value, attributes);
4202 it->WriteDataValue(value);
4203
4204 if (is_observed) {
4205 RETURN_ON_EXCEPTION(
4206 it->isolate(),
4207 EnqueueChangeRecord(object, "reconfigure", it->GetName(),
4208 it->factory()->the_hole_value()),
4209 Object);
4210 }
4211
4212 return value;
4213 }
4214
4215 case LookupIterator::DATA: {
4216 PropertyDetails details = it->property_details();
4217 Handle<Object> old_value = it->factory()->the_hole_value();
4218 // Regular property update if the attributes match.
4219 if (details.attributes() == attributes) {
4220 return SetDataProperty(it, value);
4221 }
4222
4223 // Special case: properties of typed arrays cannot be reconfigured to
4224 // non-writable nor to non-enumerable.
4225 if (it->IsElement() && (object->HasExternalArrayElements() ||
4226 object->HasFixedTypedArrayElements())) {
4227 return RedefineNonconfigurableProperty(it->isolate(), it->GetName(),
4228 value, STRICT);
4229 }
4230
4231 // Reconfigure the data property if the attributes mismatch.
4232 if (is_observed) old_value = it->GetDataValue();
4233
4234 it->ReconfigureDataProperty(value, attributes);
4235 it->WriteDataValue(value);
4236
4237 if (is_observed) {
4238 if (old_value->SameValue(*value)) {
4239 old_value = it->factory()->the_hole_value();
4240 }
4241 RETURN_ON_EXCEPTION(it->isolate(),
4242 EnqueueChangeRecord(object, "reconfigure",
4243 it->GetName(), old_value),
4244 Object);
4245 }
4246 } 4271 }
4247 } 4272 }
4248 4273
4249 return value; 4274 return AddDataProperty(it, value, attributes, STRICT,
4275 CERTAINLY_NOT_STORE_FROM_KEYED);
4250 } 4276 }
4251 4277
4252 4278
4253 // Reconfigures a property to a data property with attributes, even if it is not
4254 // reconfigurable.
4255 MaybeHandle<Object> JSObject::SetOwnPropertyIgnoreAttributes( 4279 MaybeHandle<Object> JSObject::SetOwnPropertyIgnoreAttributes(
4256 Handle<JSObject> object, Handle<Name> name, Handle<Object> value, 4280 Handle<JSObject> object, Handle<Name> name, Handle<Object> value,
4257 PropertyAttributes attributes, ExecutableAccessorInfoHandling handling) { 4281 PropertyAttributes attributes, ExecutableAccessorInfoHandling handling) {
4258 DCHECK(!value->IsTheHole()); 4282 DCHECK(!value->IsTheHole());
4259 LookupIterator it(object, name, LookupIterator::OWN_SKIP_INTERCEPTOR); 4283 LookupIterator it(object, name, LookupIterator::HIDDEN);
4260 if (it.state() == LookupIterator::ACCESS_CHECK) { 4284 return DefineOwnPropertyIgnoreAttributes(&it, value, attributes, handling);
4261 if (!it.isolate()->MayAccess(object)) {
4262 return SetPropertyWithFailedAccessCheck(&it, value, SLOPPY);
4263 }
4264 it.Next();
4265 }
4266
4267 if (it.IsFound()) {
4268 return ReconfigureAsDataProperty(&it, value, attributes, handling);
4269 }
4270
4271 return AddDataProperty(&it, value, attributes, STRICT,
4272 CERTAINLY_NOT_STORE_FROM_KEYED);
4273 } 4285 }
4274 4286
4275 4287
4276 MaybeHandle<Object> JSObject::SetOwnElementIgnoreAttributes( 4288 MaybeHandle<Object> JSObject::SetOwnElementIgnoreAttributes(
4277 Handle<JSObject> object, uint32_t index, Handle<Object> value, 4289 Handle<JSObject> object, uint32_t index, Handle<Object> value,
4278 PropertyAttributes attributes, ExecutableAccessorInfoHandling handling) { 4290 PropertyAttributes attributes, ExecutableAccessorInfoHandling handling) {
4279 Isolate* isolate = object->GetIsolate(); 4291 Isolate* isolate = object->GetIsolate();
4280 LookupIterator it(isolate, object, index, 4292 LookupIterator it(isolate, object, index, LookupIterator::HIDDEN);
4281 LookupIterator::OWN_SKIP_INTERCEPTOR); 4293 return DefineOwnPropertyIgnoreAttributes(&it, value, attributes, handling);
4282 if (it.state() == LookupIterator::ACCESS_CHECK) {
4283 if (!isolate->MayAccess(object)) {
4284 return SetPropertyWithFailedAccessCheck(&it, value, STRICT);
4285 }
4286 it.Next();
4287 }
4288
4289 if (it.IsFound()) {
4290 return ReconfigureAsDataProperty(&it, value, attributes, handling);
4291 }
4292
4293 return AddDataProperty(&it, value, attributes, STRICT,
4294 MAY_BE_STORE_FROM_KEYED);
4295 } 4294 }
4296 4295
4297 4296
4298 Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithInterceptor( 4297 Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithInterceptor(
4299 LookupIterator* it) { 4298 LookupIterator* it) {
4300 Isolate* isolate = it->isolate(); 4299 Isolate* isolate = it->isolate();
4301 // Make sure that the top context does not change when doing 4300 // Make sure that the top context does not change when doing
4302 // callbacks or interceptor calls. 4301 // callbacks or interceptor calls.
4303 AssertNoContextChange ncc(isolate); 4302 AssertNoContextChange ncc(isolate);
4304 HandleScope scope(isolate); 4303 HandleScope scope(isolate);
(...skipping 12332 matching lines...) Expand 10 before | Expand all | Expand 10 after
16637 Handle<Object> new_value) { 16636 Handle<Object> new_value) {
16638 if (cell->value() != *new_value) { 16637 if (cell->value() != *new_value) {
16639 cell->set_value(*new_value); 16638 cell->set_value(*new_value);
16640 Isolate* isolate = cell->GetIsolate(); 16639 Isolate* isolate = cell->GetIsolate();
16641 cell->dependent_code()->DeoptimizeDependentCodeGroup( 16640 cell->dependent_code()->DeoptimizeDependentCodeGroup(
16642 isolate, DependentCode::kPropertyCellChangedGroup); 16641 isolate, DependentCode::kPropertyCellChangedGroup);
16643 } 16642 }
16644 } 16643 }
16645 } // namespace internal 16644 } // namespace internal
16646 } // namespace v8 16645 } // namespace v8
OLDNEW
« no previous file with comments | « src/objects.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698