From 8fee5e810acc243f1c0467398866d382a9146003 Mon Sep 17 00:00:00 2001 From: "dgozman@chromium.org" Date: Wed, 28 Apr 2010 12:05:40 +0000 Subject: [PATCH] Added ability to remove prototype from function. In this case, [[Construct]] from function will not be allowed. Added runtime function %FunctionRemovePrototype for this. Removed prototypes from all builtin functions. Some sputnik tests marked as fixed. Added test to check builtins behavior. Review URL: http://codereview.chromium.org/1722003 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4536 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/bootstrapper.cc | 84 ++++++++++++++++++++++-------- src/contexts.h | 2 + src/factory.cc | 28 ++++++++++ src/factory.h | 8 +++ src/handles.cc | 1 + src/ic.cc | 6 ++- src/objects-inl.h | 18 +++++++ src/objects.cc | 9 ++++ src/objects.h | 12 +++++ src/runtime.cc | 23 ++++++++ src/runtime.h | 1 + src/v8natives.js | 1 + test/mjsunit/function-without-prototype.js | 54 +++++++++++++++++++ test/sputnik/sputnik.status | 73 -------------------------- 14 files changed, 223 insertions(+), 97 deletions(-) create mode 100644 test/mjsunit/function-without-prototype.js diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index ac9663d..4f0e0db 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -248,9 +248,13 @@ class Genesis BASE_EMBEDDED { void TransferNamedProperties(Handle from, Handle to); void TransferIndexedProperties(Handle from, Handle to); + enum PrototypePropertyMode { + DONT_ADD_PROTOTYPE, + ADD_READONLY_PROTOTYPE, + ADD_WRITEABLE_PROTOTYPE + }; Handle ComputeFunctionInstanceDescriptor( - bool make_prototype_read_only, - bool make_prototype_enumerable = false); + PrototypePropertyMode prototypeMode); void MakeFunctionInstancePrototypeWritable(); static bool CompileBuiltin(int index); @@ -330,7 +334,8 @@ static Handle InstallFunction(Handle target, bool is_ecma_native) { Handle symbol = Factory::LookupAsciiSymbol(name); Handle call_code = Handle(Builtins::builtin(call)); - Handle function = + Handle function = prototype.is_null() ? + Factory::NewFunctionWithoutPrototype(symbol, call_code) : Factory::NewFunctionWithPrototype(symbol, type, instance_size, @@ -346,23 +351,23 @@ static Handle InstallFunction(Handle target, Handle Genesis::ComputeFunctionInstanceDescriptor( - bool make_prototype_read_only, - bool make_prototype_enumerable) { + PrototypePropertyMode prototypeMode) { Handle result = Factory::empty_descriptor_array(); - // Add prototype. - PropertyAttributes attributes = static_cast( - (make_prototype_enumerable ? 0 : DONT_ENUM) - | DONT_DELETE - | (make_prototype_read_only ? READ_ONLY : 0)); - result = - Factory::CopyAppendProxyDescriptor( - result, - Factory::prototype_symbol(), - Factory::NewProxy(&Accessors::FunctionPrototype), - attributes); + if (prototypeMode != DONT_ADD_PROTOTYPE) { + PropertyAttributes attributes = static_cast( + DONT_ENUM | + DONT_DELETE | + (prototypeMode == ADD_READONLY_PROTOTYPE ? READ_ONLY : 0)); + result = + Factory::CopyAppendProxyDescriptor( + result, + Factory::prototype_symbol(), + Factory::NewProxy(&Accessors::FunctionPrototype), + attributes); + } - attributes = + PropertyAttributes attributes = static_cast(DONT_ENUM | DONT_DELETE | READ_ONLY); // Add length. result = @@ -407,14 +412,26 @@ Handle Genesis::CreateEmptyFunction() { // Please note that the prototype property for function instances must be // writable. Handle function_map_descriptors = - ComputeFunctionInstanceDescriptor(false, false); + ComputeFunctionInstanceDescriptor(ADD_WRITEABLE_PROTOTYPE); fm->set_instance_descriptors(*function_map_descriptors); + fm->set_function_with_prototype(true); + + // Functions with this map will not have a 'prototype' property, and + // can not be used as constructors. + fm = Factory::NewMap(JS_FUNCTION_TYPE, JSFunction::kSize); + global_context()->set_function_without_prototype_map(*fm); + function_map_descriptors = + ComputeFunctionInstanceDescriptor(DONT_ADD_PROTOTYPE); + fm->set_instance_descriptors(*function_map_descriptors); + fm->set_function_with_prototype(false); // Allocate the function map first and then patch the prototype later fm = Factory::NewMap(JS_FUNCTION_TYPE, JSFunction::kSize); global_context()->set_function_map(*fm); - function_map_descriptors = ComputeFunctionInstanceDescriptor(true); + function_map_descriptors = + ComputeFunctionInstanceDescriptor(ADD_READONLY_PROTOTYPE); fm->set_instance_descriptors(*function_map_descriptors); + fm->set_function_with_prototype(true); Handle object_name = Handle(Heap::Object_symbol()); @@ -458,6 +475,11 @@ Handle Genesis::CreateEmptyFunction() { global_context()->function_map()->set_prototype(*empty_function); global_context()->function_instance_map()->set_prototype(*empty_function); + // Allocate a distinct prototype for the function map for functions without + // prototype, so it will not add 'prototype' property in the proto chain. + global_context()->function_without_prototype_map()->set_prototype( + *Factory::NewJSObject(Top::object_function(), TENURED)); + // Allocate the function map first and then patch the prototype later Handle empty_fm = Factory::CopyMapDropDescriptors(fm); empty_fm->set_instance_descriptors(*function_map_descriptors); @@ -1215,12 +1237,12 @@ bool Genesis::InstallNatives() { // Install the call and the apply functions. Handle call = InstallFunction(proto, "call", JS_OBJECT_TYPE, JSObject::kHeaderSize, - Factory::NewJSObject(Top::object_function(), TENURED), + Handle::null(), Builtins::FunctionCall, false); Handle apply = InstallFunction(proto, "apply", JS_OBJECT_TYPE, JSObject::kHeaderSize, - Factory::NewJSObject(Top::object_function(), TENURED), + Handle::null(), Builtins::FunctionApply, false); @@ -1236,6 +1258,23 @@ bool Genesis::InstallNatives() { // Set the lengths for the functions to satisfy ECMA-262. call->shared()->set_length(1); apply->shared()->set_length(2); + + // Install the call, apply, toString and constructor properties + // for the functions without prototype. + Handle wp_proto = Handle( + JSObject::cast(Top::function_without_prototype_map()->prototype())); + + Handle call_symbol = Factory::LookupAsciiSymbol("call"); + SetProperty(wp_proto, call_symbol, call, DONT_ENUM); + + Handle apply_symbol = Factory::LookupAsciiSymbol("apply"); + SetProperty(wp_proto, apply_symbol, apply, DONT_ENUM); + + Handle to_string = GetProperty(proto, "toString"); + Handle to_string_symbol = Factory::LookupAsciiSymbol("toString"); + SetProperty(wp_proto, to_string_symbol, to_string, DONT_ENUM); + + SetProperty(wp_proto, Factory::constructor_symbol(), function, DONT_ENUM); } // Create a constructor for RegExp results (a variant of Array that @@ -1655,9 +1694,10 @@ void Genesis::MakeFunctionInstancePrototypeWritable() { HandleScope scope; Handle function_map_descriptors = - ComputeFunctionInstanceDescriptor(false); + ComputeFunctionInstanceDescriptor(ADD_WRITEABLE_PROTOTYPE); Handle fm = Factory::CopyMapDropDescriptors(Top::function_map()); fm->set_instance_descriptors(*function_map_descriptors); + fm->set_function_with_prototype(true); Top::context()->global_context()->set_function_map(*fm); } diff --git a/src/contexts.h b/src/contexts.h index ae9bd76..01bb21b 100644 --- a/src/contexts.h +++ b/src/contexts.h @@ -74,6 +74,7 @@ enum ContextLookupFlags { V(INSTANTIATE_FUN_INDEX, JSFunction, instantiate_fun) \ V(CONFIGURE_INSTANCE_FUN_INDEX, JSFunction, configure_instance_fun) \ V(FUNCTION_MAP_INDEX, Map, function_map) \ + V(FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX, Map, function_without_prototype_map) \ V(FUNCTION_INSTANCE_MAP_INDEX, Map, function_instance_map) \ V(JS_ARRAY_MAP_INDEX, Map, js_array_map)\ V(REGEXP_RESULT_MAP_INDEX, Map, regexp_result_map)\ @@ -179,6 +180,7 @@ class Context: public FixedArray { JS_ARRAY_MAP_INDEX, REGEXP_RESULT_MAP_INDEX, FUNCTION_MAP_INDEX, + FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX, FUNCTION_INSTANCE_MAP_INDEX, INITIAL_OBJECT_PROTOTYPE_INDEX, BOOLEAN_FUNCTION_INDEX, diff --git a/src/factory.cc b/src/factory.cc index 20f8261..35d3c54 100644 --- a/src/factory.cc +++ b/src/factory.cc @@ -513,6 +513,16 @@ Handle Factory::NewFunctionWithPrototype(Handle name, } +Handle Factory::NewFunctionWithoutPrototype(Handle name, + Handle code) { + Handle function = NewFunctionWithoutPrototype(name); + function->set_code(*code); + ASSERT(!function->has_initial_map()); + ASSERT(!function->has_prototype()); + return function; +} + + Handle Factory::NewCode(const CodeDesc& desc, ZoneScopeInfo* sinfo, Code::Flags flags, @@ -705,6 +715,24 @@ Handle Factory::NewFunction(Handle name, } +Handle Factory::NewFunctionWithoutPrototypeHelper( + Handle name) { + Handle function_share = NewSharedFunctionInfo(name); + CALL_HEAP_FUNCTION(Heap::AllocateFunction( + *Top::function_without_prototype_map(), + *function_share, + *the_hole_value()), + JSFunction); +} + + +Handle Factory::NewFunctionWithoutPrototype(Handle name) { + Handle fun = NewFunctionWithoutPrototypeHelper(name); + fun->set_context(Top::context()->global_context()); + return fun; +} + + Handle Factory::ToObject(Handle object) { CALL_HEAP_FUNCTION(object->ToObject(), Object); } diff --git a/src/factory.h b/src/factory.h index 0f2ae86..8a190fa 100644 --- a/src/factory.h +++ b/src/factory.h @@ -218,6 +218,8 @@ class Factory : public AllStatic { static Handle NewFunction(Handle name, Handle prototype); + static Handle NewFunctionWithoutPrototype(Handle name); + static Handle NewFunction(Handle super, bool is_global); static Handle BaseNewFunctionFromSharedFunctionInfo( @@ -291,6 +293,9 @@ class Factory : public AllStatic { Handle code, bool force_initial_map); + static Handle NewFunctionWithoutPrototype(Handle name, + Handle code); + static Handle CopyAppendProxyDescriptor( Handle array, Handle key, @@ -376,6 +381,9 @@ class Factory : public AllStatic { static Handle NewFunctionHelper(Handle name, Handle prototype); + static Handle NewFunctionWithoutPrototypeHelper( + Handle name); + static Handle CopyAppendCallbackDescriptors( Handle array, Handle descriptors); diff --git a/src/handles.cc b/src/handles.cc index 84ee20b..1d4465f 100644 --- a/src/handles.cc +++ b/src/handles.cc @@ -203,6 +203,7 @@ void FlattenString(Handle string) { Handle SetPrototype(Handle function, Handle prototype) { + ASSERT(function->should_have_prototype()); CALL_HEAP_FUNCTION(Accessors::FunctionSetPrototype(*function, *prototype, NULL), diff --git a/src/ic.cc b/src/ic.cc index eaa0554..64c3ec1 100644 --- a/src/ic.cc +++ b/src/ic.cc @@ -615,7 +615,8 @@ Object* LoadIC::Load(State state, Handle object, Handle name) { } // Use specialized code for getting prototype of functions. - if (object->IsJSFunction() && name->Equals(Heap::prototype_symbol())) { + if (object->IsJSFunction() && name->Equals(Heap::prototype_symbol()) && + JSFunction::cast(*object)->should_have_prototype()) { #ifdef DEBUG if (FLAG_trace_ic) PrintF("[LoadIC : +#prototype /function]\n"); #endif @@ -824,7 +825,8 @@ Object* KeyedLoadIC::Load(State state, } // Use specialized code for getting prototype of functions. - if (object->IsJSFunction() && name->Equals(Heap::prototype_symbol())) { + if (object->IsJSFunction() && name->Equals(Heap::prototype_symbol()) && + JSFunction::cast(*object)->should_have_prototype()) { Handle function = Handle::cast(object); Object* code = StubCache::ComputeKeyedLoadFunctionPrototype(*name, *function); diff --git a/src/objects-inl.h b/src/objects-inl.h index 621a3f8..59fdd86 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -2113,6 +2113,20 @@ bool Map::has_non_instance_prototype() { } +void Map::set_function_with_prototype(bool value) { + if (value) { + set_bit_field2(bit_field2() | (1 << kFunctionWithPrototype)); + } else { + set_bit_field2(bit_field2() & ~(1 << kFunctionWithPrototype)); + } +} + + +bool Map::function_with_prototype() { + return ((1 << kFunctionWithPrototype) & bit_field2()) != 0; +} + + void Map::set_is_access_check_needed(bool access_check_needed) { if (access_check_needed) { set_bit_field(bit_field() | (1 << kIsAccessCheckNeeded)); @@ -2568,6 +2582,10 @@ Object* JSFunction::prototype() { return instance_prototype(); } +bool JSFunction::should_have_prototype() { + return map()->function_with_prototype(); +} + bool JSFunction::is_compiled() { return shared()->is_compiled(); diff --git a/src/objects.cc b/src/objects.cc index 252d847..459c8aa 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -4906,6 +4906,7 @@ Object* JSFunction::SetInstancePrototype(Object* value) { Object* JSFunction::SetPrototype(Object* value) { + ASSERT(should_have_prototype()); Object* construct_prototype = value; // If the value is not a JSObject, store the value in the map's @@ -4931,6 +4932,14 @@ Object* JSFunction::SetPrototype(Object* value) { } +Object* JSFunction::RemovePrototype() { + ASSERT(map() == context()->global_context()->function_map()); + set_map(context()->global_context()->function_without_prototype_map()); + set_prototype_or_initial_map(Heap::the_hole_value()); + return this; +} + + Object* JSFunction::SetInstanceClassName(String* name) { shared()->set_instance_class_name(name); return this; diff --git a/src/objects.h b/src/objects.h index cdc2491..40be3a2 100644 --- a/src/objects.h +++ b/src/objects.h @@ -2854,6 +2854,12 @@ class Map: public HeapObject { inline void set_non_instance_prototype(bool value); inline bool has_non_instance_prototype(); + // Tells whether function has special prototype property. If not, prototype + // property will not be created when accessed (will return undefined), + // and construction from this function will not be allowed. + inline void set_function_with_prototype(bool value); + inline bool function_with_prototype(); + // Tells whether the instance with this map should be ignored by the // __proto__ accessor. inline void set_is_hidden_prototype() { @@ -3030,6 +3036,7 @@ class Map: public HeapObject { // Bit positions for bit field 2 static const int kIsExtensible = 0; + static const int kFunctionWithPrototype = 1; // Layout of the default cache. It holds alternating name and code objects. static const int kCodeCacheEntrySize = 2; @@ -3396,6 +3403,11 @@ class JSFunction: public JSObject { Object* SetInstancePrototype(Object* value); Object* SetPrototype(Object* value); + // After prototype is removed, it will not be created when accessed, and + // [[Construct]] from this function will not be allowed. + Object* RemovePrototype(); + inline bool should_have_prototype(); + // Accessor for this function's initial map's [[class]] // property. This is primarily used by ECMA native functions. This // method sets the class_name field of this function's initial map diff --git a/src/runtime.cc b/src/runtime.cc index 9b9acae..3fa41e9 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -1450,6 +1450,18 @@ static Object* Runtime_FunctionSetName(Arguments args) { } +static Object* Runtime_FunctionRemovePrototype(Arguments args) { + NoHandleAllocation ha; + ASSERT(args.length() == 1); + + CONVERT_CHECKED(JSFunction, f, args[0]); + Object* obj = f->RemovePrototype(); + if (obj->IsFailure()) return obj; + + return Heap::undefined_value(); +} + + static Object* Runtime_FunctionGetScript(Arguments args) { HandleScope scope; ASSERT(args.length() == 1); @@ -1523,6 +1535,7 @@ static Object* Runtime_FunctionSetPrototype(Arguments args) { ASSERT(args.length() == 2); CONVERT_CHECKED(JSFunction, fun, args[0]); + ASSERT(fun->should_have_prototype()); Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL); if (obj->IsFailure()) return obj; return args[0]; // return TOS @@ -6541,6 +6554,16 @@ static Object* Runtime_NewObject(Arguments args) { } Handle function = Handle::cast(constructor); + + // If function should not have prototype, construction is not allowed. In this + // case generated code bailouts here, since function has no initial_map. + if (!function->should_have_prototype()) { + Vector< Handle > arguments = HandleVector(&constructor, 1); + Handle type_error = + Factory::NewTypeError("not_constructor", arguments); + return Top::Throw(*type_error); + } + #ifdef ENABLE_DEBUGGER_SUPPORT // Handle stepping into constructors if step into is active. if (Debug::StepInActive()) { diff --git a/src/runtime.h b/src/runtime.h index 02df58d..d0719f6 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -184,6 +184,7 @@ namespace internal { F(FunctionSetPrototype, 2, 1) \ F(FunctionGetName, 1, 1) \ F(FunctionSetName, 2, 1) \ + F(FunctionRemovePrototype, 1, 1) \ F(FunctionGetSourceCode, 1, 1) \ F(FunctionGetScript, 1, 1) \ F(FunctionGetScriptSourcePosition, 1, 1) \ diff --git a/src/v8natives.js b/src/v8natives.js index 86d3ad8..fd86dda 100644 --- a/src/v8natives.js +++ b/src/v8natives.js @@ -54,6 +54,7 @@ function InstallFunctions(object, attributes, functions) { var key = functions[i]; var f = functions[i + 1]; %FunctionSetName(f, key); + %FunctionRemovePrototype(f); %SetProperty(object, key, f, attributes); } %ToFastProperties(object); diff --git a/test/mjsunit/function-without-prototype.js b/test/mjsunit/function-without-prototype.js new file mode 100644 index 0000000..9018086 --- /dev/null +++ b/test/mjsunit/function-without-prototype.js @@ -0,0 +1,54 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Tests that function does not have prototype. +function testPrototype(f) { + assertFalse('prototype' in f); + assertEquals(undefined, f.prototype); + f.prototype = 42; + assertEquals(42, f.prototype); + assertTrue('prototype' in f); +} + +// Tests that construction from function throws. +function testConstruction(name) { + assertThrows("new " + name + "()"); + eval(name + ".prototype = 42;"); + assertThrows("new " + name + "()"); +} + +testPrototype(eval); +testPrototype(Array.prototype.push); +testPrototype(Function.prototype.call); +testPrototype(String.fromCharCode); +var date = new Date(); +testPrototype(date.toString); + +testConstruction("parseInt"); +testConstruction("Function.prototype.apply"); +var regexp = /abc/g; +testConstruction("regexp.test"); diff --git a/test/sputnik/sputnik.status b/test/sputnik/sputnik.status index e5b9e20..4c6fd1e 100644 --- a/test/sputnik/sputnik.status +++ b/test/sputnik/sputnik.status @@ -54,79 +54,6 @@ S15.10.6.3_A1_T16: FAIL_OK S15.10.7_A1_T1: FAIL_OK S15.10.7_A1_T2: FAIL_OK -# We allow construct calls to built-in functions, and we allow built-in -# functions to have prototypes. -S15.1.2.1_A4.6: FAIL_OK -S15.1.2.1_A4.7: FAIL_OK -S15.1.2.2_A9.6: FAIL_OK -S15.1.2.2_A9.7: FAIL_OK -S15.1.2.3_A7.6: FAIL_OK -S15.1.2.3_A7.7: FAIL_OK -S15.1.2.4_A2.6: FAIL_OK -S15.1.2.4_A2.7: FAIL_OK -S15.1.2.5_A2.6: FAIL_OK -S15.1.2.5_A2.7: FAIL_OK -S15.1.3.1_A5.6: FAIL_OK -S15.1.3.1_A5.7: FAIL_OK -S15.1.3.2_A5.6: FAIL_OK -S15.1.3.2_A5.7: FAIL_OK -S15.1.3.3_A5.6: FAIL_OK -S15.1.3.3_A5.7: FAIL_OK -S15.1.3.4_A5.6: FAIL_OK -S15.1.3.4_A5.7: FAIL_OK -S15.10.6.2_A6: FAIL_OK -S15.10.6.3_A6: FAIL_OK -S15.10.6.4_A6: FAIL_OK -S15.10.6.4_A7: FAIL_OK -S15.2.4.2_A6: FAIL_OK -S15.2.4.3_A6: FAIL_OK -S15.2.4.4_A6: FAIL_OK -S15.2.4.5_A6: FAIL_OK -S15.2.4.6_A6: FAIL_OK -S15.2.4.7_A6: FAIL_OK -S15.3.4.2_A6: FAIL_OK -S15.4.4.10_A5.6: FAIL_OK -S15.4.4.10_A5.7: FAIL_OK -S15.4.4.11_A7.6: FAIL_OK -S15.4.4.11_A7.7: FAIL_OK -S15.4.4.12_A5.6: FAIL_OK -S15.4.4.12_A5.7: FAIL_OK -S15.4.4.13_A5.6: FAIL_OK -S15.4.4.13_A5.7: FAIL_OK -S15.4.4.2_A4.6: FAIL_OK -S15.4.4.3_A4.6: FAIL_OK -S15.4.4.3_A4.6: FAIL_OK -S15.4.4.4_A4.6: FAIL_OK -S15.4.4.4_A4.7: FAIL_OK -S15.4.4.5_A6.6: FAIL_OK -S15.4.4.5_A6.7: FAIL_OK -S15.4.4.6_A5.6: FAIL_OK -S15.4.4.6_A5.7: FAIL_OK -S15.4.4.7_A6.6: FAIL_OK -S15.4.4.7_A6.7: FAIL_OK -S15.4.4.8_A5.6: FAIL_OK -S15.4.4.8_A5.7: FAIL_OK -S15.4.4.9_A5.6: FAIL_OK -S15.4.4.9_A5.7: FAIL_OK -S15.5.4.10_A6: FAIL_OK -S15.5.4.11_A6: FAIL_OK -S15.5.4.12_A6: FAIL_OK -S15.5.4.13_A6: FAIL_OK -S15.5.4.14_A6: FAIL_OK -S15.5.4.15_A6: FAIL_OK -S15.5.4.16_A6: FAIL_OK -S15.5.4.17_A6: FAIL_OK -S15.5.4.18_A6: FAIL_OK -S15.5.4.19_A6: FAIL_OK -S15.5.4.4_A6: FAIL_OK -S15.5.4.5_A6: FAIL_OK -S15.5.4.6_A6: FAIL_OK -S15.5.4.7_A6: FAIL_OK -S15.5.4.9_A6: FAIL_OK -S15.3.4.3_A12: FAIL_OK -S15.3.4.4_A12: FAIL_OK -S15.5.4.8_A6: FAIL_OK - # We are silent in some regexp cases where the spec wants us to give # errors, for compatibility. S15.10.2.11_A1_T2: FAIL -- 2.7.4