ソースを参照

[jvm] add custom EReg implementation

Simon Krajewski 5 年 前
コミット
6ade79c7f2

+ 16 - 0
src/generators/genjvm.ml

@@ -1699,6 +1699,16 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return
 	method new_native_array jsig el =
 		jm#new_native_array jsig (List.map (fun e -> fun () -> self#texpr (rvalue_sig jsig) e) el)
 
+	method spawn_compiled_pattern_field (s1 : string) (s2 : string) =
+		let name = Printf.sprintf "_hx_pattern_%s_%i" (patch_name jm#get_name) jm#get_next_regex_id in
+		let jf = jc#spawn_field name NativeSignatures.haxe_compiled_pattern_sig [FdStatic;FdPrivate;FdFinal] in
+		let jm = jc#get_static_init_method in
+		jm#string s1;
+		jm#string s2;
+		jm#invokestatic NativeSignatures.haxe_ereg_path "compilePattern" (method_sig [string_sig;string_sig] (Some NativeSignatures.haxe_compiled_pattern_sig));
+		jm#putstatic jc#get_this_path jf#get_name jf#get_jsig;
+		jf
+
 	method texpr ret e =
 		try
 			if not jm#is_terminated then self#texpr' ret e
@@ -1837,6 +1847,12 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return
 			self#texpr (if need_val ret then rvalue_any else RVoid) e1;
 			(* Technically this could throw... but whatever *)
 			if need_val ret then ignore(NativeArray.create jm#get_code jc#get_pool (jsignature_of_type gctx t))
+		| TNew({cl_path=(["haxe";"root"],"EReg") as ereg_path},[],[{eexpr = TConst (TString s1)};{eexpr = TConst (TString s2)}]) when jm != jc#get_static_init_method ->
+			let jf = self#spawn_compiled_pattern_field s1 s2 in
+			jm#construct ConstructInit ereg_path (fun () ->
+				jm#getstatic jc#get_this_path jf#get_name jf#get_jsig;
+				[jf#get_jsig]
+			)
 		| TNew(c,tl,el) ->
 			begin match get_constructor (fun cf -> cf.cf_type) c with
 			|_,cf ->

+ 1 - 5
src/generators/jvm/jvmFunctions.ml

@@ -352,6 +352,7 @@ module JavaFunctionalInterfaces = struct
 end
 
 open JavaFunctionalInterfaces
+open JvmGlobals
 
 class typed_function
 	(functions : typed_functions)
@@ -363,11 +364,6 @@ class typed_function
 = object(self)
 
 	val jc_closure =
-		let patch_name name = match name with
-			| "<init>" -> "new"
-			| "<clinit>" -> "__init__"
-			| name -> name
-		in
 		let name = match kind with
 			| FuncLocal ->
 				Printf.sprintf "Closure_%s_%i" (patch_name host_method#get_name) host_method#get_next_closure_id

+ 6 - 1
src/generators/jvm/jvmGlobals.ml

@@ -163,4 +163,9 @@ let write_string ch s = IO.nwrite_string ch s
 
 let write_array16 ch f a =
 	write_ui16 ch (Array.length a);
-	Array.iter (f ch) a
+	Array.iter (f ch) a
+
+let patch_name name = match name with
+	| "<init>" -> "new"
+	| "<clinit>" -> "__init__"
+	| name -> name

+ 6 - 0
src/generators/jvm/jvmMethod.ml

@@ -143,6 +143,7 @@ class builder jc name jsig = object(self)
 	val mutable argument_locals = []
 	val mutable thrown_exceptions = Hashtbl.create 0
 	val mutable closure_count = 0
+	val mutable regex_count = 0
 
 	(* per-frame *)
 	val mutable locals = []
@@ -195,6 +196,11 @@ class builder jc name jsig = object(self)
 		closure_count <- closure_count + 1;
 		id
 
+	method get_next_regex_id =
+		let id = regex_count in
+		regex_count <- regex_count + 1;
+		id
+
 	(** Adds the current state of locals and stack as a stack frame. This has to be called on every branch target. **)
 	method add_stack_frame =
 		let locals = self#get_locals_for_stack_frame locals in

+ 6 - 0
src/generators/jvm/jvmSignature.ml

@@ -118,6 +118,12 @@ module NativeSignatures = struct
 	let haxe_function_path = (["haxe";"jvm"],"Function")
 	let haxe_function_sig = TObject(haxe_function_path,[])
 
+	let haxe_compiled_pattern_path = (["haxe";"jvm"],"CompiledPattern")
+	let haxe_compiled_pattern_sig = TObject(haxe_compiled_pattern_path,[])
+
+	let haxe_ereg_path = (["haxe";"root"],"EReg")
+	let haxe_ereg_sig = TObject(haxe_ereg_path,[])
+
 	let void_path = ["java";"lang"],"Void"
 	let void_sig = TObject(void_path,[])
 

+ 32 - 0
std/jvm/CompiledPattern.hx

@@ -0,0 +1,32 @@
+/*
+ * Copyright (C)2005-2020 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package jvm;
+
+import java.util.regex.Pattern;
+
+@:structInit
+@:native("haxe.jvm.CompiledPattern")
+class CompiledPattern {
+	public final pattern:Pattern;
+	public final isGlobal:Bool;
+}

+ 167 - 0
std/jvm/_std/EReg.hx

@@ -0,0 +1,167 @@
+/*
+ * Copyright (C)2005-2019 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+import java.util.regex.*;
+import jvm.CompiledPattern;
+
+using StringTools;
+
+@:coreApi class EReg {
+	private var matcher:Matcher;
+	private var cur:String;
+	private var isGlobal:Bool;
+
+	@:overload
+	public function new(r:String, opt:String) {
+		initialize(compilePattern(r, opt));
+	}
+
+	@:overload
+	function new(compiledPattern:CompiledPattern) {
+		initialize(compiledPattern);
+	}
+
+	function initialize(compiledPattern:CompiledPattern):Void {
+		matcher = compiledPattern.pattern.matcher("");
+		isGlobal = compiledPattern.isGlobal;
+	}
+
+	public function match(s:String):Bool {
+		cur = s;
+		matcher = matcher.reset(s);
+		return matcher.find();
+	}
+
+	public function matched(n:Int):String {
+		if (n == 0)
+			return matcher.group();
+		else
+			return matcher.group(n);
+	}
+
+	public function matchedLeft():String {
+		return untyped cur.substring(0, matcher.start());
+	}
+
+	public function matchedRight():String {
+		return untyped cur.substring(matcher.end(), cur.length);
+	}
+
+	public function matchedPos():{pos:Int, len:Int} {
+		var start = matcher.start();
+		return {pos: start, len: matcher.end() - start};
+	}
+
+	public function matchSub(s:String, pos:Int, len:Int = -1):Bool {
+		matcher = matcher.reset(len < 0 ? s : s.substr(0, pos + len));
+		cur = s;
+		return matcher.find(pos);
+	}
+
+	public function split(s:String):Array<String> {
+		if (isGlobal) {
+			var ret = [];
+			while (this.match(s)) {
+				ret.push(matchedLeft());
+				s = matchedRight();
+			}
+			ret.push(s);
+			return ret;
+		} else {
+			var m = matcher;
+			m.reset(s);
+			if (m.find()) {
+				return untyped [s.substring(0, m.start()), s.substring(m.end(), s.length)];
+			} else {
+				return [s];
+			}
+		}
+	}
+
+	inline function start(group:Int):Int {
+		return matcher.start(group);
+	}
+
+	inline function len(group:Int):Int {
+		return matcher.end(group) - matcher.start(group);
+	}
+
+	public function replace(s:String, by:String):String {
+		matcher.reset(s);
+		by = by.replace("\\", "\\\\").replace("$$", "\\$");
+		return isGlobal ? matcher.replaceAll(by) : matcher.replaceFirst(by);
+	}
+
+	public function map(s:String, f:EReg->String):String {
+		var offset = 0;
+		var buf = new StringBuf();
+		do {
+			if (offset >= s.length)
+				break;
+			else if (!matchSub(s, offset)) {
+				buf.add(s.substr(offset));
+				break;
+			}
+			var p = matchedPos();
+			buf.add(s.substr(offset, p.pos - offset));
+			buf.add(f(this));
+			if (p.len == 0) {
+				buf.add(s.substr(p.pos, 1));
+				offset = p.pos + 1;
+			} else
+				offset = p.pos + p.len;
+		} while (isGlobal);
+		if (!isGlobal && offset > 0 && offset < s.length)
+			buf.add(s.substr(offset));
+		return buf.toString();
+	}
+
+	public static inline function escape(s:String):String {
+		return Pattern.quote(s);
+	}
+
+	static function compilePattern(r:String, opt:String):CompiledPattern {
+		var flags = 0;
+		var isGlobal = false;
+		for (i in 0...opt.length) {
+			switch (StringTools.fastCodeAt(opt, i)) {
+				case 'i'.code:
+					flags |= Pattern.CASE_INSENSITIVE;
+				case 'm'.code:
+					flags |= Pattern.MULTILINE;
+				case 's'.code:
+					flags |= Pattern.DOTALL;
+				case 'g'.code:
+					isGlobal = true;
+			}
+		}
+
+		flags |= Pattern.UNICODE_CASE;
+		#if !android // see https://github.com/HaxeFoundation/haxe/issues/7632
+		flags |= Pattern.UNICODE_CHARACTER_CLASS;
+		#end
+		return {
+			pattern: Pattern.compile(r, flags),
+			isGlobal: isGlobal
+		}
+	}
+}