-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathSubUtil.java
More file actions
194 lines (179 loc) · 7.49 KB
/
SubUtil.java
File metadata and controls
194 lines (179 loc) · 7.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
package org.perlonjava.runtime.perlmodule;
import org.perlonjava.runtime.runtimetypes.*;
import static org.perlonjava.runtime.runtimetypes.RuntimeScalarType.*;
/**
* Sub::Util module implementation for PerlOnJava.
* Provides utility functions for working with subroutines.
*/
public class SubUtil extends PerlModuleBase {
/**
* Constructor for SubUtil.
*/
public SubUtil() {
super("Sub::Util");
}
/**
* Static initializer to set up the Sub::Util module.
*/
public static void initialize() {
SubUtil subUtil = new SubUtil();
subUtil.initializeExporter();
// Set $VERSION so CPAN.pm can detect our bundled version
GlobalVariable.getGlobalVariable("Sub::Util::VERSION").set(new RuntimeScalar("1.70"));
subUtil.defineExport("EXPORT_OK", "prototype", "set_prototype", "subname", "set_subname");
try {
subUtil.registerMethod("prototype", "$");
subUtil.registerMethod("set_prototype", null); // No prototype to allow @_ passing
subUtil.registerMethod("subname", "$");
subUtil.registerMethod("set_subname", null); // No prototype to allow @_ passing
// Phase D-W2c: B.pm consults `Sub::Name::_is_renamed` to know
// whether to honor a Sub::Util::set_subname rename in
// `B::CV->GV->NAME`. Expose `Sub::Util::_is_renamed` (and
// alias `Sub::Name::_is_renamed`) so set_subname is reflected
// by Class::MOP::get_code_info even if the renamed sub was
// never installed into the target package's stash.
subUtil.registerMethod("_is_renamed", "$");
} catch (NoSuchMethodException e) {
System.err.println("Warning: Missing Sub::Util method: " + e.getMessage());
}
}
/**
* Returns the prototype of a subroutine.
*
* @param args The arguments: a CODE reference
* @param ctx The context
* @return The prototype string or undef
*/
public static RuntimeList prototype(RuntimeArray args, int ctx) {
if (args.size() != 1) {
throw new IllegalStateException("Bad number of arguments for prototype()");
}
RuntimeScalar codeRef = args.get(0);
if (codeRef.type != CODE) {
return new RuntimeScalar().getList(); // undef for non-CODE
}
RuntimeCode code = (RuntimeCode) codeRef.value;
String proto = code.prototype;
if (proto == null) {
return new RuntimeScalar().getList(); // undef
}
return new RuntimeScalar(proto).getList();
}
/**
* Sets the prototype of a subroutine.
*
* @param args The arguments: prototype string, CODE reference
* @param ctx The context
* @return The CODE reference
*/
public static RuntimeList set_prototype(RuntimeArray args, int ctx) {
if (args.size() != 2) {
throw new IllegalStateException("Bad number of arguments for set_prototype()");
}
RuntimeScalar protoScalar = args.get(0);
RuntimeScalar codeRef = args.get(1);
if (codeRef.type != CODE) {
throw new IllegalArgumentException("set_prototype requires a CODE reference");
}
RuntimeCode code = (RuntimeCode) codeRef.value;
if (protoScalar.type == UNDEF) {
code.prototype = null;
} else {
code.prototype = protoScalar.toString();
}
return codeRef.getList();
}
/**
* Returns the name of a subroutine.
*
* @param args The arguments: a CODE reference
* @param ctx The context
* @return The name of the subroutine
*/
public static RuntimeList subname(RuntimeArray args, int ctx) {
if (args.size() != 1) {
throw new IllegalStateException("Bad number of arguments for subname()");
}
RuntimeScalar codeRef = args.get(0);
if (codeRef.type != CODE) {
return new RuntimeScalar().getList(); // undef for non-CODE
}
RuntimeCode code = (RuntimeCode) codeRef.value;
String sub = code.subName;
boolean renamed = code.explicitlyRenamed;
String pkg = code.packageName;
if (renamed) {
// Honor explicit rename; sub may be empty string.
if (sub == null) sub = "";
if (pkg != null && !pkg.isEmpty()) {
return new RuntimeScalar(pkg + "::" + sub).getList();
}
return new RuntimeScalar(sub).getList();
}
if (sub == null || sub.isEmpty()) {
// Anonymous sub: real Perl returns "Package::__ANON__" where Package
// is the compile-time package (CvSTASH).
// Honor `local *PKG::__ANON__ = 'name'` by consulting the package's
// *__ANON__ glob's nameOverride. See dev/modules/anon_sub_naming.md.
if (pkg != null && !pkg.isEmpty()) {
org.perlonjava.runtime.runtimetypes.RuntimeGlob anonGlob =
GlobalVariable.peekGlobalIO(pkg + "::__ANON__");
if (anonGlob != null && anonGlob.nameOverride != null
&& !anonGlob.nameOverride.isEmpty()) {
return new RuntimeScalar(pkg + "::" + anonGlob.nameOverride).getList();
}
return new RuntimeScalar(pkg + "::__ANON__").getList();
}
return new RuntimeScalar("__ANON__").getList();
}
if (pkg != null && !pkg.isEmpty()) {
return new RuntimeScalar(pkg + "::" + sub).getList();
}
return new RuntimeScalar(sub).getList();
}
/**
* Sets the name of a subroutine.
*
* @param args The arguments: name string, CODE reference
* @param ctx The context
* @return The CODE reference
*/
public static RuntimeList set_subname(RuntimeArray args, int ctx) {
if (args.size() != 2) {
throw new IllegalStateException("Bad number of arguments for set_subname()");
}
RuntimeScalar nameScalar = args.get(0);
RuntimeScalar codeRef = args.get(1);
if (codeRef.type != CODE) {
throw new IllegalArgumentException("set_subname requires a CODE reference");
}
RuntimeCode code = (RuntimeCode) codeRef.value;
String fullName = nameScalar.toString();
// Parse package::subname format
int lastColon = fullName.lastIndexOf("::");
if (lastColon >= 0) {
code.packageName = fullName.substring(0, lastColon);
code.subName = fullName.substring(lastColon + 2);
} else {
code.subName = fullName;
}
// Mark the CV as explicitly renamed so B::svref_2object()->GV->NAME
// honors the assigned name even when no matching stash entry exists.
code.explicitlyRenamed = true;
return codeRef.getList();
}
/**
* Phase D-W2c: returns true if {@code set_subname} has been called on
* the given coderef. Used by {@code B::CV->_introspect} to decide
* whether to honor the renamed name.
*/
public static RuntimeList _is_renamed(RuntimeArray args, int ctx) {
if (args.size() != 1) {
throw new IllegalStateException("Bad number of arguments for _is_renamed()");
}
RuntimeScalar codeRef = args.get(0);
if (codeRef.type != CODE) return new RuntimeScalar(0).getList();
RuntimeCode code = (RuntimeCode) codeRef.value;
return new RuntimeScalar(code.explicitlyRenamed ? 1 : 0).getList();
}
}