Index: src/objects.cc |
diff --git a/src/objects.cc b/src/objects.cc |
index defde30c44468f4b6190a14dabddcce05dd1284a..9feea5091c860fe0bb06d66591e93595f6e3b4c3 100644 |
--- a/src/objects.cc |
+++ b/src/objects.cc |
@@ -8681,6 +8681,17 @@ Handle<Map> Map::CopyNormalized(Handle<Map> map, |
return result; |
} |
+// Return an immutable prototype exotic object version of the input map. |
+// This is not cached in the transition tree because it is only used on |
Toon Verwaest
2016/06/30 15:35:33
The comment is a little inaccurate. The JS_GLOBAL_
Dan Ehrenberg
2016/06/30 23:12:41
Changed the comment; WDYT?
|
+// the global object, and excluding it saves memory on the map transition |
+// tree. |
+ |
+// static |
+Handle<Map> Map::TransitionToImmutableProto(Handle<Map> map) { |
+ Handle<Map> new_map = Map::Copy(map, "ImmutablePrototype"); |
+ new_map->set_immutable_proto(true); |
+ return new_map; |
+} |
Handle<Map> Map::CopyInitialMap(Handle<Map> map, int instance_size, |
int in_object_properties, |
@@ -14973,6 +14984,13 @@ Maybe<bool> JSObject::SetPrototype(Handle<JSObject> object, |
// Nothing to do if prototype is already set. |
if (map->prototype() == *value) return Just(true); |
+ bool immutable_proto = object->map()->is_immutable_proto(); |
+ if (immutable_proto) { |
+ RETURN_FAILURE( |
+ isolate, should_throw, |
+ NewTypeError(MessageTemplate::kImmutablePrototypeSet, object)); |
+ } |
+ |
// From 8.6.2 Object Internal Methods |
// ... |
// In addition, if [[Extensible]] is false the value of the [[Class]] and |
@@ -15024,6 +15042,45 @@ Maybe<bool> JSObject::SetPrototype(Handle<JSObject> object, |
return Just(true); |
} |
+// static |
+Maybe<bool> JSObject::SetImmutableProto(Handle<JSObject> object, |
+ bool from_javascript, |
+ ShouldThrow should_throw) { |
+ Isolate* isolate = object->GetIsolate(); |
+ |
+ if (from_javascript) { |
Toon Verwaest
2016/06/30 15:35:33
I guess this shouldn't be possible? So shouldn't b
Dan Ehrenberg
2016/06/30 23:12:41
Good point, nicer to not have that code. I include
|
+ if (object->IsAccessCheckNeeded() && |
+ !isolate->MayAccess(handle(isolate->context()), object)) { |
+ isolate->ReportFailedAccessCheck(object); |
+ RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); |
+ RETURN_FAILURE(isolate, should_throw, |
+ NewTypeError(MessageTemplate::kNoAccess)); |
+ } |
+ } else { |
+ DCHECK(!object->IsAccessCheckNeeded()); |
+ } |
+ |
+ Handle<JSObject> real_receiver = object; |
+ if (from_javascript) { |
+ // Find the first object in the chain whose prototype object is not |
+ // hidden. |
+ PrototypeIterator iter(isolate, real_receiver, kStartAtPrototype, |
+ PrototypeIterator::END_AT_NON_HIDDEN); |
+ while (!iter.IsAtEnd()) { |
+ // Casting to JSObject is fine because hidden prototypes are never |
+ // JSProxies. |
+ real_receiver = PrototypeIterator::GetCurrent<JSObject>(iter); |
+ iter.Advance(); |
+ } |
+ } |
+ Handle<Map> map(real_receiver->map()); |
+ |
+ // Nothing to do if prototype is already set. |
+ if (map->is_immutable_proto()) return Just(true); |
+ Handle<Map> new_map = Map::TransitionToImmutableProto(map); |
+ object->set_map(*new_map); |
+ return Just(true); |
+} |
void JSObject::EnsureCanContainElements(Handle<JSObject> object, |
Arguments* args, |