6195ea86bc
In the VM, factory constructors always had an extra "type arguments" parameter, even if class is not generic. Factory constructor bodies were using class type parameters instead of function type parameters. This results in extra code when calling non-generic factories which is slightly inefficient in terms of code size and performance. Also, it creates an additional complexity throughout the system as factories should be special cased in many places. This change removes artificial "type arguments" parameter, treating factory constructors basically as static methods. This matches kernel AST representation. TEST=ci Change-Id: I957583cb2ce9a3c408699880a04036e06b01dd31 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/501762 Reviewed-by: Slava Egorov <vegorov@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
175 lines
6.5 KiB
C++
175 lines
6.5 KiB
C++
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#include "vm/regexp/regexp.h"
|
|
#include "platform/assert.h"
|
|
#include "vm/bootstrap_natives.h"
|
|
#include "vm/canonical_tables.h"
|
|
#include "vm/exceptions.h"
|
|
#include "vm/native_entry.h"
|
|
#include "vm/object.h"
|
|
#include "vm/object_store.h"
|
|
#include "vm/regexp/regexp-parser.h"
|
|
#include "vm/reusable_handles.h"
|
|
#include "vm/symbols.h"
|
|
#include "vm/thread.h"
|
|
|
|
namespace dart {
|
|
|
|
DEFINE_NATIVE_ENTRY(RegExp_factory, 0, 5) {
|
|
GET_NON_NULL_NATIVE_ARGUMENT(String, pattern, arguments->NativeArgAt(0));
|
|
|
|
bool multi_line = arguments->NativeArgAt(1) == Bool::True().ptr();
|
|
bool ignore_case = arguments->NativeArgAt(2) != Bool::True().ptr();
|
|
bool unicode = arguments->NativeArgAt(3) == Bool::True().ptr();
|
|
bool dot_all = arguments->NativeArgAt(4) == Bool::True().ptr();
|
|
|
|
RegExpFlags flags;
|
|
flags |= RegExpFlag::kGlobal; // All dart regexps are global.
|
|
if (ignore_case) flags |= RegExpFlag::kIgnoreCase;
|
|
if (multi_line) flags |= RegExpFlag::kMultiline;
|
|
if (unicode) flags |= RegExpFlag::kUnicode;
|
|
if (dot_all) flags |= RegExpFlag::kDotAll;
|
|
|
|
RegExpKey lookup_key(pattern, flags);
|
|
RegExp& regexp = RegExp::Handle(thread->zone());
|
|
{
|
|
REUSABLE_OBJECT_HANDLESCOPE(thread);
|
|
REUSABLE_SMI_HANDLESCOPE(thread);
|
|
REUSABLE_WEAK_ARRAY_HANDLESCOPE(thread);
|
|
Object& key = thread->ObjectHandle();
|
|
Smi& value = thread->SmiHandle();
|
|
WeakArray& data = thread->WeakArrayHandle();
|
|
data = thread->isolate_group()->object_store()->regexp_table();
|
|
CanonicalRegExpSet table(&key, &value, &data);
|
|
regexp ^= table.GetOrNull(lookup_key);
|
|
table.Release();
|
|
if (!regexp.IsNull()) {
|
|
return regexp.ptr();
|
|
}
|
|
}
|
|
|
|
// Parse the pattern once in order to throw any format exceptions within
|
|
// the factory constructor. It is parsed again upon compilation.
|
|
RegExpCompileData compileData;
|
|
// Throws an exception on parsing failure.
|
|
if (!RegExpParser::ParseRegExpFromHeapString(isolate, zone, pattern, flags,
|
|
&compileData)) {
|
|
USE(RegExpStatics::ThrowRegExpException(isolate, flags, pattern,
|
|
compileData.error));
|
|
UNREACHABLE();
|
|
}
|
|
|
|
{
|
|
RegExpKey lookup_symbol_key(String::Handle(Symbols::New(thread, pattern)),
|
|
flags);
|
|
SafepointMutexLocker ml(thread->isolate_group()->symbols_mutex());
|
|
CanonicalRegExpSet table(
|
|
thread->zone(),
|
|
thread->isolate_group()->object_store()->regexp_table());
|
|
regexp ^= table.InsertNewOrGet(lookup_symbol_key);
|
|
thread->isolate_group()->object_store()->set_regexp_table(table.Release());
|
|
}
|
|
|
|
ASSERT(regexp.flags() == flags);
|
|
return regexp.ptr();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(RegExp_getPattern, 0, 1) {
|
|
const RegExp& regexp = RegExp::CheckedHandle(zone, arguments->NativeArgAt(0));
|
|
ASSERT(!regexp.IsNull());
|
|
return regexp.pattern();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(RegExp_getIsMultiLine, 0, 1) {
|
|
const RegExp& regexp = RegExp::CheckedHandle(zone, arguments->NativeArgAt(0));
|
|
ASSERT(!regexp.IsNull());
|
|
return Bool::Get(IsMultiline(regexp.flags())).ptr();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(RegExp_getIsUnicode, 0, 1) {
|
|
const RegExp& regexp = RegExp::CheckedHandle(zone, arguments->NativeArgAt(0));
|
|
ASSERT(!regexp.IsNull());
|
|
return Bool::Get(IsUnicode(regexp.flags())).ptr();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(RegExp_getIsDotAll, 0, 1) {
|
|
const RegExp& regexp = RegExp::CheckedHandle(zone, arguments->NativeArgAt(0));
|
|
ASSERT(!regexp.IsNull());
|
|
return Bool::Get(IsDotAll(regexp.flags())).ptr();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(RegExp_getIsCaseSensitive, 0, 1) {
|
|
const RegExp& regexp = RegExp::CheckedHandle(zone, arguments->NativeArgAt(0));
|
|
ASSERT(!regexp.IsNull());
|
|
return Bool::Get(!IsIgnoreCase(regexp.flags())).ptr();
|
|
}
|
|
|
|
static ObjectPtr ThrowUninitialized(const RegExp& regexp) {
|
|
const String& pattern = String::Handle(regexp.pattern());
|
|
const String& errmsg =
|
|
String::Handle(String::New("Regular expression is not initialized yet."));
|
|
const String& message = String::Handle(String::Concat(errmsg, pattern));
|
|
const Array& args = Array::Handle(Array::New(1));
|
|
args.SetAt(0, message);
|
|
Exceptions::ThrowByType(Exceptions::kState, args);
|
|
return Object::null();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(RegExp_getGroupCount, 0, 1) {
|
|
const RegExp& regexp = RegExp::CheckedHandle(zone, arguments->NativeArgAt(0));
|
|
ASSERT(!regexp.IsNull());
|
|
if (regexp.num_bracket_expressions() != -1) {
|
|
return Smi::New(regexp.num_bracket_expressions());
|
|
}
|
|
return ThrowUninitialized(regexp);
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(RegExp_getGroupNameMap, 0, 1) {
|
|
const RegExp& regexp = RegExp::CheckedHandle(zone, arguments->NativeArgAt(0));
|
|
ASSERT(!regexp.IsNull());
|
|
if (regexp.num_bracket_expressions<std::memory_order_acquire>() != -1) {
|
|
return regexp.capture_name_map();
|
|
}
|
|
return ThrowUninitialized(regexp);
|
|
}
|
|
|
|
static ObjectPtr ExecuteMatch(Thread* thread,
|
|
Zone* zone,
|
|
NativeArguments* arguments,
|
|
bool sticky) {
|
|
const RegExp& regexp = RegExp::CheckedHandle(zone, arguments->NativeArgAt(0));
|
|
ASSERT(!regexp.IsNull());
|
|
GET_NON_NULL_NATIVE_ARGUMENT(String, subject, arguments->NativeArgAt(1));
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Smi, start_index, arguments->NativeArgAt(2));
|
|
|
|
// Both generated code and the interpreter are using 32-bit registers and
|
|
// 32-bit backtracking stack so they can't work with strings which are
|
|
// larger than that. Validate these assumptions before running the regexp.
|
|
if (!Utils::IsInt(32, subject.Length())) {
|
|
Exceptions::ThrowRangeError("length",
|
|
Integer::Handle(Integer::New(subject.Length())),
|
|
0, kMaxInt32);
|
|
}
|
|
if (!Utils::IsInt(32, start_index.Value())) {
|
|
Exceptions::ThrowRangeError("start_index", Integer::Cast(start_index),
|
|
kMinInt32, kMaxInt32);
|
|
}
|
|
|
|
return RegExpStatics::Interpret(thread, regexp, subject, start_index.Value(),
|
|
sticky);
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(RegExp_ExecuteMatch, 0, 3) {
|
|
// This function is intrinsified. See Intrinsifier::RegExp_ExecuteMatch.
|
|
return ExecuteMatch(thread, zone, arguments, /*sticky=*/false);
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(RegExp_ExecuteMatchSticky, 0, 3) {
|
|
// This function is intrinsified. See Intrinsifier::RegExp_ExecuteMatchSticky.
|
|
return ExecuteMatch(thread, zone, arguments, /*sticky=*/true);
|
|
}
|
|
|
|
} // namespace dart
|