// Complete a first-kinded type structure. If type definition is higher kinded, this will // complete an instance of the type at the type arguments. Otherwise, this will complete // the type definition itself. private void BuildTypeExpression(Seq<JST.Statement> body, JST.Expression lhs) { TypeCompEnv.BindUsage(body, CollectPhase1Usage(), TypePhase.Id); // TODO: Replace with prototype body.Add(JST.Statement.DotCall(RootId.ToE(), Constants.RootSetupTypeDefaults, TypeId.ToE())); EmitBaseAndSupertypes(body, lhs); EmitDefaultConstructor(body, lhs); EmitMemberwiseClone(body, lhs); EmitClone(body, lhs); EmitDefaultValue(body, lhs); EmitStaticMethods(body, lhs); EmitConstructObjectAndInstanceMethods(body, lhs); EmitVirtualAndInterfaceMethodRedirectors(body, lhs); EmitSetupType(body, lhs); EmitUnbox(body, lhs); EmitBox(body, lhs); EmitUnboxAny(body, lhs); EmitConditionalDeref(body, lhs); EmitIsValue(body, lhs); EmitEquals(body, lhs); EmitHash(body, lhs); EmitInterop(body, lhs); }
private TypeCompilerEnvironment (CST.Global global, IImSeq<CST.SkolemDef> skolemDefs, CST.AssemblyDef assembly, CST.TypeDef type, IImSeq<CST.TypeRef> typeBoundArguments, CompilerEnvironment env, JST.NameSupply nameSupply, JST.Identifier rootId, JST.Identifier assemblyId, JST.Identifier typeId, IImSeq<JST.Identifier> typeBoundTypeParameterIds, TypeTrace typeTrace) : base( global, skolemDefs, assembly, type, typeBoundArguments) { this.env = env; NameSupply = nameSupply; this.rootId = rootId; this.assemblyId = assemblyId; this.typeId = typeId; TypeBoundTypeParameterIds = typeBoundTypeParameterIds; boundAssemblies = new Map<CST.AssemblyName, JST.Expression>(); boundTypes = new Map<CST.TypeRef, ExpressionAndPhase>(); this.typeTrace = typeTrace; }
public TypeRepresentation(InstanceState state, int numExportsBoundToInstance, int numStepsToRootType, JST.Expression keyField, JST.Expression typeClassifier, bool undefininedIsNotNull) { State = state; NumExportsBoundToInstance = numExportsBoundToInstance; NumStepsToRootType = numStepsToRootType; KeyField = keyField; TypeClassifier = typeClassifier; UndefininedIsNotNull = undefininedIsNotNull; }
private void EmitDefaultConstructor(Seq<JST.Statement> body, JST.Expression lhs) { if (Parent.DefaultConstructor != null) { var innerNameSupply = NameSupply.Fork(); var innerBody = new Seq<JST.Statement>(); var ctor = Env.JSTHelpers.ConstructorExpression (TypeCompEnv, innerNameSupply, innerBody, null, Parent.DefaultConstructor, JST.Constants.EmptyExpressions); innerBody.Add(new JST.ReturnStatement(ctor)); body.Add (JST.Statement.DotAssignment (lhs, Constants.TypeDefaultConstructor, new JST.FunctionExpression(null, new JST.Statements(innerBody)))); } // else: leave undefined }
// ---------------------------------------------------------------------- // Methods // ---------------------------------------------------------------------- // Emit bindings for static or instance methods, but not for virtuals or interface methods // - Invoked from TypeDefinitionCompiler for higher-kinded type definitions // - Invoked from TypeCompiler for first-kinded type definitions public void EmitMethods(Seq<JST.Statement> body, JST.Expression lhs, JST.NameSupply outerNameSupply, JST.Expression target, bool isStatic) { switch (Env.CompilationMode) { case CompilationMode.Plain: { // Method definitions are bound directly into target foreach (var methodDef in Methods.Where(m => m.Invalid == null)) { if (Env.InteropManager.IsStatic(TyconEnv.Assembly, TyconEnv.Type, methodDef) == isStatic) { var compiler = new MethodCompiler(this, outerNameSupply, methodDef, MethodCompilationMode.DirectBind); compiler.Emit(body, target); } } break; } case CompilationMode.Collecting: { // Method definitions are bound into MethodCache, redirectors are bound into target foreach (var methodDef in Methods.Where(m => m.Invalid == null)) { if (Env.InteropManager.IsStatic(TyconEnv.Assembly, TyconEnv.Type, methodDef) == isStatic) { var slot = Env.GlobalMapping.ResolveMethodDefToSlot(TyconEnv.Assembly, TyconEnv.Type, methodDef); var methodName = CST.CSTWriter.WithAppend (Env.Global, CST.WriterStyle.Uniform, methodDef.MethodSignature.Append); body.Add (JST.Statement.DotCall (RootId.ToE(), Constants.RootCollectingBindMethodBuilder, lhs, new JST.BooleanLiteral(isStatic), new JST.StringLiteral(slot), new JST.StringLiteral(methodName))); var compiler = new MethodCompiler(this, outerNameSupply, methodDef, MethodCompilationMode.DirectBind); compiler.Emit(body, JST.Expression.Dot(target, Constants.TypeMethodCache)); } } break; } case CompilationMode.Traced: { // Methods in the initial trace or this trace will be bound directly. // Methods in a trace other than above are bound via builder which is given trace name. // Remaining methods are built via builder with null trace name. var traceToArgs = new Map<string, Seq<JST.Expression>>(); var remainingArgs = new Seq<JST.Expression>(); remainingArgs.Add(TypeDefinitionId.ToE()); remainingArgs.Add(new JST.BooleanLiteral(isStatic)); remainingArgs.Add(new JST.NullExpression()); foreach (var methodDef in Methods.Where(m => m.Invalid == null)) { if (Env.InteropManager.IsStatic(TyconEnv.Assembly, TyconEnv.Type, methodDef) == isStatic) { var slot = Env.GlobalMapping.ResolveMethodDefToSlot(TyconEnv.Assembly, TyconEnv.Type, methodDef); var defTrace = Env.Traces.MethodToTrace[methodDef.QualifiedMemberName(Env.Global, TyconEnv.Assembly, TyconEnv.Type)]; if (defTrace.Flavor == TraceFlavor.OnDemand && defTrace != TypeTrace.Parent.Parent) { // Method definition in in another trace, bind redirector for it. var args = default(Seq<JST.Expression>); if (!traceToArgs.TryGetValue(defTrace.Name, out args)) { args = new Seq<JST.Expression>(); args.Add(lhs); args.Add(new JST.BooleanLiteral(isStatic)); args.Add(new JST.StringLiteral(defTrace.Name)); traceToArgs.Add(defTrace.Name, args); } args.Add(new JST.StringLiteral(slot)); } else if (defTrace.Flavor == TraceFlavor.Remainder) // Method definition is in a stand-alone loader, bind redirector for it. remainingArgs.Add(new JST.StringLiteral(slot)); else { // Method definition is bound directly var compiler = new MethodCompiler(this, outerNameSupply, methodDef, MethodCompilationMode.DirectBind); compiler.Emit(body, target); } } } foreach (var kv in traceToArgs) body.Add(JST.Statement.DotCall(RootId.ToE(), Constants.RootBindMethodBuilders, kv.Value)); if (remainingArgs.Count > 3) body.Add(JST.Statement.DotCall(RootId.ToE(), Constants.RootBindMethodBuilders, remainingArgs)); break; } default: throw new ArgumentOutOfRangeException(); } }
// Take acccount of: // - PassRootAsArgument // - PassInstanceAsArgument // - InlineParamsArray private JST.Expression AppendFinalImport(JST.NameSupply nameSupply, JST.Identifier rootId, CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, CST.MethodDef methodDef, JST.Expression script, ISeq<JST.Statement> body, IImSeq<JST.Expression> arguments) { var isInstanceMethod = !(methodDef.IsStatic || methodDef.IsConstructor); var scriptExpectsRoot = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ImportAttributeRef, attributeHelper.ThePassRootAsArgumentProperty, true, false, ref scriptExpectsRoot); var passInstAsArg = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ImportAttributeRef, attributeHelper.ThePassInstanceAsArgumentProperty, true, false, ref passInstAsArg); var instanceIsThis = isInstanceMethod && !passInstAsArg; var inlineParams = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ImportAttributeRef, attributeHelper.TheInlineParamsArrayProperty, true, false, ref inlineParams); var lastArgIsParamsArray = methodDef.HasParamsArray(rootEnv) && inlineParams; var funcScript = script as JST.FunctionExpression; var nextArg = 0; var instArg = default(JST.Expression); if (instanceIsThis) { // Instance argument will be the first arg to 'call' or 'apply', or the target of a '.' call. instArg = arguments[nextArg++]; if (lastArgIsParamsArray && !instArg.IsDuplicatable) { // Make sure instance argument is evaluated before the remaining arguments var instId = nameSupply.GenSym(); body.Add(JST.Statement.Var(instId, instArg)); instArg = instId.ToE(); } } else { if (lastArgIsParamsArray) instArg = new JST.NullExpression(); } var knownArgs = 0; var call = default(JST.Expression); if (lastArgIsParamsArray) { // We mush build script args at runtime var argsId = nameSupply.GenSym(); body.Add(JST.Statement.Var(argsId, new JST.ArrayLiteral())); if (scriptExpectsRoot) { body.Add(JST.Statement.DotCall(argsId.ToE(), Constants.push, rootId.ToE())); knownArgs++; } while (nextArg < arguments.Count - 1) { body.Add(JST.Statement.DotCall(argsId.ToE(), Constants.push, arguments[nextArg++])); knownArgs++; } var arrArg = arguments[nextArg]; if (!arrArg.IsDuplicatable) { var arrId = nameSupply.GenSym(); body.Add(JST.Statement.Var(arrId, arrArg)); arrArg = arrId.ToE(); } var iId = nameSupply.GenSym(); body.Add (new JST.IfStatement (JST.Expression.IsNotNull(arrArg), new JST.Statements (new JST.ForStatement (new JST.ForVarLoopClause (iId, new JST.NumericLiteral(0), new JST.BinaryExpression (iId.ToE(), JST.BinaryOp.LessThan, JST.Expression.Dot(arrArg, Constants.length)), new JST.UnaryExpression(iId.ToE(), JST.UnaryOp.PostIncrement)), new JST.Statements (JST.Statement.DotCall (argsId.ToE(), Constants.push, new JST.IndexExpression(arrArg, iId.ToE()))))))); if (funcScript != null) { // var args = ...; var x = script; x.apply(this/null, args) var scriptId = nameSupply.GenSym(); body.Add(JST.Statement.Var(scriptId, funcScript)); call = JST.Expression.DotCall(scriptId.ToE(), Constants.apply, instArg, argsId.ToE()); } else { if (instanceIsThis) { // var args = ...; (this.script).apply(this, args); call = JST.Expression.DotCall (JST.Expression.Dot(instArg, JST.Expression.ExplodePath(script)), Constants.apply, instArg, argsId.ToE()); } else { // var args = ...; script.apply(null, args) call = JST.Expression.DotCall(script, Constants.apply, instArg, argsId.ToE()); } } } else { var callArgs = new Seq<JST.Expression>(); if (instanceIsThis && funcScript != null) callArgs.Add(instArg); if (scriptExpectsRoot) { callArgs.Add(rootId.ToE()); knownArgs++; } while (nextArg < arguments.Count) { callArgs.Add(arguments[nextArg++]); knownArgs++; } if (instanceIsThis) { if (funcScript != null) { // var x = script; x.call(this, arg1, ..., argn) var scriptId = nameSupply.GenSym(); body.Add(JST.Statement.Var(scriptId, funcScript)); call = JST.Expression.DotCall(scriptId.ToE(), Constants.call, callArgs); } else // this.script(arg1, ..., angn) call = new JST.CallExpression (JST.Expression.Dot(instArg, JST.Expression.ExplodePath(script)), callArgs); } else // script(arg1, ..., argn) call = new JST.CallExpression(script, callArgs); } if (funcScript != null) { if (funcScript.Parameters.Count < knownArgs) { var ctxt = CST.MessageContextBuilders.Member(env.Global, assemblyDef, typeDef, methodDef); env.Log(new InvalidInteropMessage(ctxt, "script accepts too few arguments")); throw new DefinitionException(); } } return call; }
public void AppendExport(JST.NameSupply nameSupply, JST.Identifier rootId, CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, CST.MethodDef methodDef, JST.Expression instance, Seq<JST.Statement> body, AppendCallExported appendCallExported) { var ctxt = CST.MessageContextBuilders.Member(env.Global, assemblyDef, typeDef, methodDef); if (methodDef.TypeArity > 0) { env.Log(new InvalidInteropMessage(ctxt, "polymorphic methods cannot be exported")); throw new DefinitionException(); } if (typeDef.Arity > 0 && (methodDef.IsConstructor || methodDef.IsStatic)) { env.Log (new InvalidInteropMessage (ctxt, "higher-kinded types cannot export static methods or instance constructors")); throw new DefinitionException(); } CheckParameterAndReturnTypesAreImportableExportable(assemblyDef, typeDef, methodDef); var script = default(JST.Expression); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ExportAttributeRef, attributeHelper.TheScriptProperty, true, false, ref script); var outer = typeDef.OuterPropertyOrEvent(methodDef.MethodSignature); if (!methodDef.IsStatic && methodDef.IsConstructor) { // Constructors if (methodDef.TypeArity > 0) throw new InvalidOperationException("invalid constructor"); } else if (outer != null) { var localScript = default(JST.Expression); var hasLocalScript = attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ExportAttributeRef, attributeHelper.TheScriptProperty, false, false, ref localScript); switch (outer.Flavor) { case CST.MemberDefFlavor.Event: { var eventDef = (CST.EventDef)outer; if (eventDef.Add != null && methodDef.Signature.Equals(eventDef.Add)) // Adder script = hasLocalScript ? GetterSetterAdderRemoverNameFromMethod (assemblyDef, typeDef, methodDef, "add", localScript) : GetterSetterAdderRemoverNameFromPropertyEvent (assemblyDef, typeDef, methodDef, "add", script); else if (eventDef.Remove != null && methodDef.Signature.Equals(eventDef.Remove)) // Remover script = hasLocalScript ? GetterSetterAdderRemoverNameFromMethod (assemblyDef, typeDef, methodDef, "remove", localScript) : GetterSetterAdderRemoverNameFromPropertyEvent (assemblyDef, typeDef, methodDef, "remove", script); else throw new InvalidOperationException(); break; } case CST.MemberDefFlavor.Property: { var propDef = (CST.PropertyDef)outer; if (propDef.Get != null && methodDef.Signature.Equals(propDef.Get)) // Getter script = hasLocalScript ? GetterSetterAdderRemoverNameFromMethod (assemblyDef, typeDef, methodDef, "get", localScript) : GetterSetterAdderRemoverNameFromPropertyEvent (assemblyDef, typeDef, methodDef, "get", script); else if (propDef.Set != null && methodDef.Signature.Equals(propDef.Set)) // Setter script = hasLocalScript ? GetterSetterAdderRemoverNameFromMethod (assemblyDef, typeDef, methodDef, "set", localScript) : GetterSetterAdderRemoverNameFromPropertyEvent (assemblyDef, typeDef, methodDef, "set", script); else throw new InvalidOperationException(); break; } case CST.MemberDefFlavor.Field: case CST.MemberDefFlavor.Method: throw new InvalidOperationException("not a property or event"); default: throw new ArgumentOutOfRangeException(); } } else { // Normal methods script = RecaseMethod(assemblyDef, typeDef, methodDef, script); } script = PrefixName(assemblyDef, typeDef, methodDef, script, true); AppendFinalExport (nameSupply, rootId, assemblyDef, typeDef, methodDef, script, instance, body, appendCallExported); }
// Take account of: // - BindToPrototype // - PassRootAsArgument // - PassInstanceAsArgument // - InlineParamsArray private void AppendFinalExport(JST.NameSupply nameSupply, JST.Identifier rootId, CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, CST.MethodDef methodDef, JST.Expression script, JST.Expression instance, ISeq<JST.Statement> body, AppendCallExported appendCallExported) { if (script == null) throw new InvalidOperationException("expecting default script value"); var ctxt = CST.MessageContextBuilders.Member(env.Global, assemblyDef, typeDef, methodDef); var isInstance = !methodDef.IsStatic && !methodDef.IsConstructor; if (isInstance) { if (typeDef.Style is CST.ValueTypeStyle) { env.Log(new InvalidInteropMessage(ctxt, "cannot export instance methods from value types")); throw new DefinitionException(); } } var inlineParams = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ExportAttributeRef, attributeHelper.TheInlineParamsArrayProperty, true, false, ref inlineParams); var lastArgIsParamsArray = methodDef.HasParamsArray(rootEnv) && inlineParams; var isPassRoot = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ExportAttributeRef, attributeHelper.ThePassRootAsArgumentProperty, true, false, ref isPassRoot); var isProto = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ExportAttributeRef, attributeHelper.TheBindToPrototypeProperty, true, false, ref isProto); var isPassInstance = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ExportAttributeRef, attributeHelper.ThePassInstanceAsArgumentProperty, true, false, ref isPassInstance); var bindToInstance = isInstance && !isProto; if (bindToInstance != (instance != null)) throw new InvalidOperationException("expecting instance"); var captureThis = isInstance && !isPassInstance; var funcScript = script as JST.FunctionExpression; // Build the function to export var funcBody = new Seq<JST.Statement>(); var funcParameters = new Seq<JST.Identifier>(); var funcCallArgs = new Seq<JST.Expression>(); var funcArity = methodDef.Arity; if ((methodDef.IsConstructor && !methodDef.IsStatic) || captureThis) // unmanaged will not pass the instance funcArity--; if (lastArgIsParamsArray) // managed params args will be extracted from remainder of unmanaged arguments funcArity--; if (captureThis) funcCallArgs.Add(new JST.ThisExpression()); for (var i = 0; i < funcArity; i++) { var id = nameSupply.GenSym(); funcParameters.Add(id); funcCallArgs.Add(id.ToE()); } if (lastArgIsParamsArray) { var iId = nameSupply.GenSym(); var arrId = nameSupply.GenSym(); funcBody.Add(JST.Statement.Var(arrId, new JST.ArrayLiteral())); funcBody.Add (new JST.ForStatement (new JST.ForVarLoopClause (iId, new JST.NumericLiteral(funcArity), new JST.BinaryExpression (iId.ToE(), JST.BinaryOp.LessThan, JST.Expression.Dot(Constants.arguments.ToE(), Constants.length)), new JST.UnaryExpression(iId.ToE(), JST.UnaryOp.PostIncrement)), new JST.Statements (JST.Statement.DotCall (arrId.ToE(), Constants.push, new JST.IndexExpression(Constants.arguments.ToE(), iId.ToE()))))); funcCallArgs.Add(arrId.ToE()); } appendCallExported(nameSupply, assemblyDef, typeDef, methodDef, funcBody, funcCallArgs); var func = new JST.FunctionExpression(funcParameters, new JST.Statements(funcBody)); // Export the above function if (funcScript != null) { var scriptArgs = new Seq<JST.Expression>(); if (isPassRoot) scriptArgs.Add(rootId.ToE()); if (bindToInstance) scriptArgs.Add(instance); scriptArgs.Add(func); if (funcScript.Parameters.Count != scriptArgs.Count) { env.Log(new InvalidInteropMessage(ctxt, "invalid function arity")); throw new DefinitionException(); } body.Add(new JST.ExpressionStatement(new JST.CallExpression(script, scriptArgs))); } else { if (bindToInstance) script = JST.Expression.Dot(instance, JST.Expression.ExplodePath(script)); EnsurePathExists(body, script, !bindToInstance); body.Add(JST.Statement.Assignment(script, func)); } }
private JST.Expression RecasePropertyEvent(CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, CST.MethodDef methodDef, JST.Expression script) { var outer = typeDef.OuterPropertyOrEvent(methodDef.MethodSignature); if (outer == null) throw new ArgumentException("not a getter/setter/adder/remover method"); if (script != null) return script; var casing = default(Casing); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.NamingAttributeRef, attributeHelper.TheMemberNameCasingProperty, true, false, ref casing); return new JST.Identifier(JST.Lexemes.StringToIdentifier(Recase(outer.Name, casing))).ToE(); }
private void EmitUnboxAny(Seq<JST.Statement> body, JST.Expression lhs) { var s = TypeCompEnv.Type.Style; if (!(s is CST.HandleTypeStyle || s is CST.ReferenceTypeStyle)) { var innerNameSupply = NameSupply.Fork(); var parameters = new Seq<JST.Identifier>(); parameters.Add(innerNameSupply.GenSym()); var obj = parameters[0].ToE(); var innerBody = new Seq<JST.Statement>(); if (s is CST.VoidTypeStyle || s is CST.ManagedPointerTypeStyle) innerBody.Add (new JST.ThrowStatement (JST.Expression.DotCall(RootId.ToE(), Constants.RootInvalidOperationException))); else if (s is CST.NullableTypeStyle) { innerBody.Add (new JST.IfStatement (JST.Expression.IsNull(obj), new JST.Statements(new JST.ReturnStatement(new JST.NullExpression())))); innerBody.Add (new JST.IfStatement (new JST.BinaryExpression (JST.Expression.Dot(obj, Constants.ObjectType), JST.BinaryOp.StrictNotEquals, TypeCompEnv.ResolveType(TypeCompEnv.TypeBoundArguments[0], TypePhase.Id)), new JST.Statements(new JST.ThrowStatement (JST.Expression.DotCall(RootId.ToE(), Constants.RootInvalidCastException))))); innerBody.Add(new JST.ReturnStatement(JST.Expression.DotCall(obj, Constants.PointerRead))); } else { innerBody.Add (new JST.IfStatement (JST.Expression.IsNull(obj), new JST.Statements(new JST.ThrowStatement (JST.Expression.DotCall(RootId.ToE(), Constants.RootNullReferenceException))))); innerBody.Add (new JST.IfStatement (new JST.BinaryExpression (JST.Expression.Dot(obj, Constants.ObjectType), JST.BinaryOp.StrictNotEquals, lhs), new JST.Statements(new JST.ThrowStatement (JST.Expression.DotCall(RootId.ToE(), Constants.RootInvalidCastException))))); innerBody.Add(new JST.ReturnStatement(JST.Expression.DotCall(obj, Constants.PointerRead))); } body.Add (JST.Statement.DotAssignment (lhs, Constants.TypeUnboxAny, new JST.FunctionExpression(parameters, new JST.Statements(innerBody)))); } // else: default is ok }
private void EmitStaticMethods(Seq<JST.Statement> body, JST.Expression lhs) { if (Env.DebugMode) body.Add(new JST.CommentStatement("Static methods")); if (TypeCompEnv.Type.Arity > 0) EmitFKToHKMethodRedirectors(body, lhs, true); else Parent.EmitMethods(body, lhs, NameSupply, lhs, true); }
private void EmitConstructObjectAndInstanceMethods(Seq<JST.Statement> body, JST.Expression lhs) { var s = TypeCompEnv.Type.Style; var ctor = default(JST.Expression); var preserve = false; if (s is CST.StringTypeStyle) { // SPECIAL CASE: System.String is represented as JavaScript String if (Env.DebugMode) body.Add(new JST.CommentStatement("ConstructObject supplied by JavaScript")); ctor = Constants.String.ToE(); body.Add(JST.Statement.DotAssignment(lhs, Constants.TypeConstructObject, ctor)); preserve = true; } else if (TypeCompEnv.TypeRef.Equals(Env.Global.ArrayRef)) { // SPECIAL CASE: System.Array is represented as JavaScript Array // (Built-in arrays and multi-dimensional arrays are also JavaScipt array's, but // we explicity overwrite their Type field to capture their exact type.) if (Env.DebugMode) body.Add(new JST.CommentStatement("ConstructObject supplied by JavaScript")); ctor = Constants.Array.ToE(); body.Add(JST.Statement.DotAssignment(lhs, Constants.TypeConstructObject, ctor)); preserve = true; } else if (TypeCompEnv.TypeRef.Equals(Env.Global.MulticastDelegateRef)) { // SPECIAL CASE: System.MulticastDelegate is represented as JavaScript Function // (Instance of derived delegate types overwrite their Type field to capture their exact type) // We bind into String's prototype if (Env.DebugMode) body.Add(new JST.CommentStatement("ConstructObject supplied by JavaScript")); ctor = Constants.Function.ToE(); body.Add(JST.Statement.DotAssignment(lhs, Constants.TypeConstructObject, ctor)); preserve = true; } else if (s is CST.InterfaceTypeStyle || s is CST.VoidTypeStyle || (TypeCompEnv.Type.IsAbstract && TypeCompEnv.Type.IsSealed)) { if (Env.DebugMode) body.Add(new JST.CommentStatement("ConstructObject should never be called")); // SPECIAL CASE: Interface types, Void and 'static' types have no instances body.Add (JST.Statement.DotAssignment (lhs, Constants.TypeConstructObject, new JST.FunctionExpression (null, new JST.Statements(new JST.ThrowStatement (JST.Expression.DotCall(RootId.ToE(), Constants.RootInvalidOperationException)))))); // Shouldn't have any instance fields or methods to bind return; } else { if (Env.DebugMode) body.Add(new JST.CommentStatement("ConstructObject")); body.Add(JST.Statement.DotAssignment(lhs, Constants.TypeConstructObject, ConstructObjectFunction())); ctor = JST.Expression.Dot(lhs, Constants.TypeConstructObject); } if (TypeCompEnv.Type.Extends != null) { if (Env.DebugMode) body.Add(new JST.CommentStatement("Inherited prototype")); if (preserve) { // The constructor's prototype object is fixed, so we must copy all fields of the // base type's prototype object into existing prototype object var baseCtor = JST.Expression.Dot(lhs, Constants.TypeBaseType, Constants.TypeConstructObject); body.Add (JST.Statement.DotCall (RootId.ToE(), Constants.RootInheritPrototypeProperties, JST.Expression.Dot(ctor, Constants.prototype), JST.Expression.Dot(baseCtor, Constants.prototype))); } else { // Use base type's ConstructObject to build our prototype object // (but supress it's field initialization) var newProto = new JST.NewExpression (new JST.CallExpression (JST.Expression.Dot(lhs, Constants.TypeBaseType, Constants.TypeConstructObject), new JST.BooleanLiteral(true))); body.Add(JST.Statement.Assignment(JST.Expression.Dot(ctor, Constants.prototype), newProto)); } } // Setup prototype var protoId = NameSupply.GenSym(); body.Add(JST.Statement.Var(protoId, JST.Expression.Dot(ctor, Constants.prototype))); var proto = protoId.ToE(); if (TypeCompEnv.Type.Extends != null && !preserve) { // Overwrite prototype's constructor body.Add(JST.Statement.DotAssignment(proto, Constants.constructor, ctor)); } // else: original constructor is valid // Type body.Add(JST.Statement.DotAssignment(proto, Constants.ObjectType, lhs)); // MethodCache body.Add(JST.Statement.DotAssignment(proto, Constants.TypeMethodCache, new JST.ObjectLiteral())); // Bind normal instance methods if (Env.DebugMode) body.Add(new JST.CommentStatement("Instance methods")); if (TypeCompEnv.Type.Arity > 0) EmitFKToHKMethodRedirectors(body, lhs, false); else Parent.EmitMethods(body, lhs, NameSupply, proto, false); }
// For speed we inline rather than chain to base-type default values // (Though, actually, that's not necessary since value types are sealed and have not inherited fields...) private void EmitDefaultValue(Seq<JST.Statement> body, JST.Expression lhs) { var s = TypeCompEnv.Type.Style; var innerBody = new Seq<JST.Statement>(); if (s is CST.VoidTypeStyle || s is CST.ManagedPointerTypeStyle) innerBody.Add (new JST.ThrowStatement (JST.Expression.DotCall(RootId.ToE(), Constants.RootInvalidOperationException))); else if (s is CST.NumberTypeStyle || s is CST.EnumTypeStyle || TypeCompEnv.TypeRef.Equals(Env.Global.DecimalRef)) innerBody.Add(new JST.ReturnStatement(new JST.NumericLiteral(0))); else if (s is CST.StructTypeStyle) { var allFieldRefs = new Seq<CST.FieldRef>(); AccumInstanceFields(TypeCompEnv, allFieldRefs); var innerTypeCompEnv = TypeCompEnv.EnterFunction(); var usage = new CST.Usage(); foreach (var fieldRef in allFieldRefs) { if (Env.JSTHelpers.DefaultFieldValueIsNonNull(innerTypeCompEnv, fieldRef)) fieldRef.ExternalFieldType.AccumUsage(usage, true); } innerTypeCompEnv.BindUsage(innerBody, usage, TypePhase.Constructed); var bindings = new OrdMap<JST.Identifier, JST.Expression>(); foreach (var fieldRef in allFieldRefs) bindings.Add (Env.JSTHelpers.ResolveFieldToIdentifier(innerTypeCompEnv, fieldRef, false), Env.JSTHelpers.DefaultFieldValue(innerTypeCompEnv, fieldRef)); innerBody.Add(new JST.ReturnStatement(new JST.ObjectLiteral(bindings))); } // else: default default value of null is ok if (innerBody.Count > 0) body.Add (JST.Statement.DotAssignment (lhs, Constants.TypeDefaultValue, new JST.FunctionExpression(null, new JST.Statements(innerBody)))); }
private void EmitClone(Seq<JST.Statement> body, JST.Expression lhs) { if (Env.JSTHelpers.CloneIsNonTrivial(TypeCompEnv, TypeCompEnv.TypeRef)) { // Same as MemberwiseClone body.Add (JST.Statement.DotAssignment (lhs, Constants.TypeClone, JST.Expression.Dot(lhs, Constants.TypeMemberwiseClone))); } // else: default identify function is ok }
// For speed we inline rather than chain to base-type clones private void EmitMemberwiseClone(Seq<JST.Statement> body, JST.Expression lhs) { var s = TypeCompEnv.Type.Style; if (s is CST.ClassTypeStyle || s is CST.StructTypeStyle) { var fields = new Seq<CST.FieldRef>(); AccumInstanceFields(TypeCompEnv, fields); var trivFields = new Seq<CST.FieldRef>(); var nonTrivFields = new Seq<CST.FieldRef>(); foreach (var fieldRef in fields) { var fieldType = ((CST.FieldSignature)fieldRef.ExternalSignature).FieldType; if (Env.JSTHelpers.CloneIsNonTrivial(TypeCompEnv, fieldType)) nonTrivFields.Add(fieldRef); else trivFields.Add(fieldRef); } if (nonTrivFields.Count > 0) { var innerTypeCompEnv = TypeCompEnv.EnterFunction(); var parameters = new Seq<JST.Identifier>(); parameters.Add(innerTypeCompEnv.NameSupply.GenSym()); var oldObj = parameters[0].ToE(); var innerBody = new Seq<JST.Statement>(); var usage = new CST.Usage(); foreach (var fieldRef in nonTrivFields) fieldRef.ExternalFieldType.AccumUsage(usage, true); innerTypeCompEnv.BindUsage(innerBody, usage, TypePhase.Constructed); var newObjId = innerTypeCompEnv.NameSupply.GenSym(); if (s is CST.ClassTypeStyle) // Reference type, object Id is allocated lazily innerBody.Add (JST.Statement.Var (newObjId, JST.Expression.DotCall(TypeId.ToE(), Constants.TypeConstructObject))); else // Value type innerBody.Add(JST.Statement.Var(newObjId, new JST.ObjectLiteral())); var newObj = newObjId.ToE(); // Explicity clone non-trivial fields foreach (var fieldRef in nonTrivFields) { var fieldId = Env.JSTHelpers.ResolveFieldToIdentifier(innerTypeCompEnv, fieldRef, false); innerBody.Add (JST.Statement.DotAssignment (newObj, fieldId, Env.JSTHelpers.CloneExpressionForType (innerTypeCompEnv, fieldRef.ExternalFieldType, JST.Expression.Dot(oldObj, fieldId)))); } if (trivFields.Count < 3) { // Explicity copy the remaining trivial fields foreach (var fieldRef in trivFields) { var fieldId = Env.JSTHelpers.ResolveFieldToIdentifier(innerTypeCompEnv, fieldRef, false); innerBody.Add (JST.Statement.DotAssignment(newObj, fieldId, JST.Expression.Dot(oldObj, fieldId))); } } else { // Generically copy the remaining trivial fields innerBody.Add (JST.Statement.DotCall(RootId.ToE(), Constants.RootInheritProperties, newObj, oldObj)); } innerBody.Add(new JST.ReturnStatement(newObj)); body.Add (JST.Statement.DotAssignment (lhs, Constants.TypeMemberwiseClone, new JST.FunctionExpression(parameters, new JST.Statements(innerBody)))); } else { // default generic clone with no inner cloning is ok return; } } else { var innerNameSupply = NameSupply.Fork(); var parameters = new Seq<JST.Identifier>(); parameters.Add(innerNameSupply.GenSym()); var oldObj = parameters[0].ToE(); var innerBody = new Seq<JST.Statement>(); if (s is CST.VoidTypeStyle) { innerBody.Add (new JST.ThrowStatement (JST.Expression.DotCall(RootId.ToE(), Constants.RootInvalidOperationException))); } else if (s is CST.ArrayTypeStyle) { var newObjId = innerNameSupply.GenSym(); innerBody.Add (JST.Statement.Var (newObjId, new JST.NewExpression (new JST.CallExpression (Constants.Array.ToE(), JST.Expression.Dot(oldObj, Constants.length))))); innerBody.Add(JST.Statement.DotAssignment(newObjId.ToE(), Constants.ObjectType, TypeId.ToE())); // Object Id is allocated lazily var iId = innerNameSupply.GenSym(); var loopClause = new JST.ForVarLoopClause (iId, new JST.BinaryExpression (iId.ToE(), JST.BinaryOp.LessThan, JST.Expression.Dot(oldObj, Constants.length)), new JST.UnaryExpression(iId.ToE(), JST.UnaryOp.PostIncrement)); var loopBody = JST.Statement.IndexAssignment (newObjId.ToE(), iId.ToE(), Env.JSTHelpers.CloneExpressionForType (TypeCompEnv, TypeCompEnv.TypeBoundArguments[0], new JST.IndexExpression(oldObj, iId.ToE()))); innerBody.Add(new JST.ForStatement(loopClause, new JST.Statements(loopBody))); innerBody.Add(new JST.ReturnStatement(newObjId.ToE())); } else if (s is CST.NullableTypeStyle) { innerBody.Add (new JST.IfStatement (JST.Expression.IsNull(oldObj), new JST.Statements(new JST.ReturnStatement(new JST.NullExpression())), new JST.Statements(new JST.ReturnStatement (Env.JSTHelpers.CloneExpressionForType (TypeCompEnv, TypeCompEnv.TypeBoundArguments[0], oldObj))))); } else { innerBody.Add(new JST.ReturnStatement(oldObj)); } body.Add (JST.Statement.DotAssignment (lhs, Constants.TypeMemberwiseClone, new JST.FunctionExpression(parameters, new JST.Statements(innerBody)))); } }
public static TypeCompilerEnvironment EnterType (CompilerEnvironment env, JST.NameSupply nameSupply, JST.Identifier rootId, JST.Identifier assemblyId, JST.Identifier typeId, CST.TypeEnvironment typeEnv, TypeTrace typeTrace) { var typeBoundTypeParameterIds = new Seq<JST.Identifier>(); for (var i = 0; i < typeEnv.Type.Arity; i++) typeBoundTypeParameterIds.Add(nameSupply.GenSym()); var res = new TypeCompilerEnvironment (typeEnv.Global, typeEnv.SkolemDefs, typeEnv.Assembly, typeEnv.Type, typeEnv.TypeBoundArguments, env, nameSupply, rootId, assemblyId, typeId, typeBoundTypeParameterIds, typeTrace); res.BindSpecial(); return res; }
public JST.Expression AppendImport(JST.NameSupply nameSupply, JST.Identifier rootId, CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, CST.MethodDef methodDef, ISeq<JST.Statement> body, IImSeq<JST.Expression> arguments) { var ctxt = CST.MessageContextBuilders.Member(env.Global, assemblyDef, typeDef, methodDef); CheckParameterAndReturnTypesAreImportableExportable(assemblyDef, typeDef, methodDef); var script = default(JST.Expression); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ImportAttributeRef, attributeHelper.TheScriptProperty, true, false, ref script); if (!methodDef.IsStatic && methodDef.IsConstructor) { // Constructor if (script == null) { var creation = default(Creation); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ImportAttributeRef, attributeHelper.TheCreationProperty, true, false, ref creation); switch (creation) { case Creation.Constructor: script = PrefixName(assemblyDef, typeDef, methodDef, null, false); break; case Creation.Object: if (arguments.Count > 0) { env.Log (new InvalidInteropMessage (ctxt, "imported constructors for object literals cannot have arguments")); throw new DefinitionException(); } script = Constants.Object.ToE(); break; case Creation.Array: script = Constants.Array.ToE(); break; default: throw new ArgumentOutOfRangeException(); } var call = AppendFinalImport (nameSupply, rootId, assemblyDef, typeDef, methodDef, script, body, arguments); return new JST.NewExpression(call); } else if (script is JST.FunctionExpression) return AppendFinalImport (nameSupply, rootId, assemblyDef, typeDef, methodDef, script, body, arguments); else { script = PrefixName(assemblyDef, typeDef, methodDef, script, false); var call = AppendFinalImport (nameSupply, rootId, assemblyDef, typeDef, methodDef, script, body, arguments); return new JST.NewExpression(call); } } else { var outer = typeDef.OuterPropertyOrEvent(methodDef.MethodSignature); if (outer != null) { var isOnMethod = attributeHelper.MethodHasAttribute (assemblyDef, typeDef, methodDef, attributeHelper.ImportAttributeRef, false, false); var localScript = default(JST.Expression); if (isOnMethod) attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ImportAttributeRef, attributeHelper.TheScriptProperty, false, false, ref localScript); switch (outer.Flavor) { case CST.MemberDefFlavor.Property: { var propDef = (CST.PropertyDef)outer; if (propDef.Get != null && methodDef.Signature.Equals(propDef.Get)) { // Getter if (isOnMethod) { script = PrefixName (assemblyDef, typeDef, methodDef, GetterSetterAdderRemoverNameFromMethod (assemblyDef, typeDef, methodDef, "get", localScript), false); return AppendFinalImport (nameSupply, rootId, assemblyDef, typeDef, methodDef, script, body, arguments); } else if (script != null && script is JST.FunctionExpression) { env.Log (new InvalidInteropMessage (ctxt, "property import script cannot be a function")); throw new DefinitionException(); } else { if (script == null && arguments.Count == 2 && !methodDef.IsStatic) return new JST.IndexExpression(arguments[0], arguments[1]); else { script = PrefixName (assemblyDef, typeDef, methodDef, RecasePropertyEvent(assemblyDef, typeDef, methodDef, script), false); if (methodDef.IsStatic && arguments.Count == 0) return script; else if (!methodDef.IsStatic && arguments.Count == 1) return JST.Expression.Dot (arguments[0], JST.Expression.ExplodePath(script)); else { env.Log (new InvalidInteropMessage (ctxt, "additional getter parameters not supported for default getters")); throw new DefinitionException(); } } } } else if (propDef.Set != null && methodDef.Signature.Equals(propDef.Set)) { // Setter if (isOnMethod) { script = PrefixName (assemblyDef, typeDef, methodDef, GetterSetterAdderRemoverNameFromMethod (assemblyDef, typeDef, methodDef, "set", localScript), false); return AppendFinalImport (nameSupply, rootId, assemblyDef, typeDef, methodDef, script, body, arguments); } else if (script != null && script is JST.FunctionExpression) { env.Log (new InvalidInteropMessage (ctxt, "property import script cannot be a function")); throw new DefinitionException(); } else { if (script == null && arguments.Count == 3 && !methodDef.IsStatic) return new JST.BinaryExpression (new JST.IndexExpression(arguments[0], arguments[1]), JST.BinaryOp.Assignment, arguments[2]); else { script = PrefixName (assemblyDef, typeDef, methodDef, RecasePropertyEvent(assemblyDef, typeDef, methodDef, script), false); if (methodDef.IsStatic && arguments.Count == 1) return new JST.BinaryExpression (script, JST.BinaryOp.Assignment, arguments[0]); else if (!methodDef.IsStatic && arguments.Count == 2) return new JST.BinaryExpression (JST.Expression.Dot(arguments[0], JST.Expression.ExplodePath(script)), JST.BinaryOp.Assignment, arguments[1]); else { env.Log (new InvalidInteropMessage (ctxt, "additional setter parameters not supported for default setters")); throw new DefinitionException(); } } } } else throw new InvalidOperationException(); } case CST.MemberDefFlavor.Event: { var eventDef = (CST.EventDef)outer; // XREF1201 if (eventDef.Add != null && methodDef.Signature.Equals(eventDef.Add)) { // Adder if (isOnMethod) { script = PrefixName (assemblyDef, typeDef, methodDef, GetterSetterAdderRemoverNameFromMethod (assemblyDef, typeDef, methodDef, "add", localScript), false); return AppendFinalImport (nameSupply, rootId, assemblyDef, typeDef, methodDef, script, body, arguments); } else if (script != null && script is JST.FunctionExpression) { env.Log (new InvalidInteropMessage(ctxt, "event import script cannot be a function")); throw new DefinitionException(); } else { // The delegate argument has already taken account of the combine, so // just a field assignment script = PrefixName (assemblyDef, typeDef, methodDef, RecasePropertyEvent(assemblyDef, typeDef, methodDef, script), false); if (methodDef.IsStatic && arguments.Count == 1) return new JST.BinaryExpression(script, JST.BinaryOp.Assignment, arguments[0]); else if (!methodDef.IsStatic && arguments.Count == 2) return new JST.BinaryExpression (JST.Expression.Dot(arguments[0], JST.Expression.ExplodePath(script)), JST.BinaryOp.Assignment, arguments[1]); else throw new InvalidOperationException("mismatched event adder arity"); } } else if (eventDef.Remove != null && methodDef.Signature.Equals(eventDef.Remove)) { // Remover if (isOnMethod) { script = PrefixName (assemblyDef, typeDef, methodDef, GetterSetterAdderRemoverNameFromMethod (assemblyDef, typeDef, methodDef, "remove", localScript), false); return AppendFinalImport (nameSupply, rootId, assemblyDef, typeDef, methodDef, script, body, arguments); } else if (script != null && script is JST.FunctionExpression) { env.Log (new InvalidInteropMessage(ctxt, "event import script cannot be a function")); throw new DefinitionException(); } else { // The delegate argument has already taken account of the delete, so // just a field assignment script = PrefixName (assemblyDef, typeDef, methodDef, RecasePropertyEvent(assemblyDef, typeDef, methodDef, script), false); if (methodDef.IsStatic && arguments.Count == 1) return new JST.BinaryExpression(script, JST.BinaryOp.Assignment, arguments[0]); else if (!methodDef.IsStatic && arguments.Count == 2) return new JST.BinaryExpression (JST.Expression.Dot(arguments[0], JST.Expression.ExplodePath(script)), JST.BinaryOp.Assignment, arguments[1]); else throw new InvalidOperationException("mismatched event remover arity"); } } else throw new InvalidOperationException(); } case CST.MemberDefFlavor.Field: case CST.MemberDefFlavor.Method: throw new InvalidOperationException("outer is not property or event"); default: throw new ArgumentOutOfRangeException(); } } else { // Normal method script = PrefixName (assemblyDef, typeDef, methodDef, RecaseMethod(assemblyDef, typeDef, methodDef, script), false); return AppendFinalImport (nameSupply, rootId, assemblyDef, typeDef, methodDef, script, body, arguments); } } }
private void EmitConditionalDeref(Seq<JST.Statement> body, JST.Expression lhs) { var s = TypeCompEnv.Type.Style; if (!(s is CST.HandleTypeStyle || s is CST.ReferenceTypeStyle)) { var innerNameSupply = NameSupply.Fork(); var parameters = new Seq<JST.Identifier>(); parameters.Add(innerNameSupply.GenSym()); var obj = parameters[0].ToE(); var innerBody = new Seq<JST.Statement>(); if (s is CST.VoidTypeStyle) innerBody.Add (new JST.ThrowStatement (JST.Expression.DotCall(RootId.ToE(), Constants.RootInvalidOperationException))); else innerBody.Add(new JST.ReturnStatement(obj)); body.Add (JST.Statement.DotAssignment (lhs, Constants.TypeConditionalDeref, new JST.FunctionExpression(parameters, new JST.Statements(innerBody)))); } // else: default is ok }
private void EnsurePathExists(ISeq<JST.Statement> statements, JST.Expression script, bool isStatic) { var path = JST.Expression.ExplodePath(script); for (var i = isStatic ? 0 : 1; i < path.Count - 1; i++) { var prefixPath = new Seq<JST.PropertyName>(); for (var j = 0; j <= i; j++) prefixPath.Add(path[j]); var prefix = JST.Expression.Path(prefixPath); if (i == 0) { var exId = new JST.Identifier("e"); #if !JSCRIPT_IS_CORRECT statements.Add(JST.Statement.Var(exId)); #endif statements.Add (new JST.TryStatement (new JST.Statements(new JST.ExpressionStatement(prefix)), new JST.CatchClause (exId, new JST.Statements(JST.Statement.Assignment(prefix, new JST.ObjectLiteral()))))); } else if (!path[i].Value.Equals(Constants.prototype.Value, StringComparison.Ordinal)) statements.Add (new JST.IfStatement (JST.Expression.IsNull(prefix), new JST.Statements(JST.Statement.Assignment(prefix, new JST.ObjectLiteral())))); } }
private void EmitIsValue(Seq<JST.Statement> body, JST.Expression lhs) { var s = TypeCompEnv.Type.Style; if (s is CST.HandleTypeStyle || s is CST.ReferenceTypeStyle || s is CST.ManagedPointerTypeStyle) // default is ok return; else if (s is CST.VoidTypeStyle) body.Add(JST.Statement.DotAssignment(lhs, Constants.TypeIsValueType, new JST.NullExpression())); else body.Add(JST.Statement.DotAssignment(lhs, Constants.TypeIsValueType, new JST.BooleanLiteral(true))); }
// Return the name for a getter/setter/adder/remover method based on the user-supplied method name or underlying method name private JST.Expression GetterSetterAdderRemoverNameFromMethod(CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, CST.MethodDef methodDef, string prefix, JST.Expression script) { if (script != null) return script; var name = methodDef.Name; if (name.StartsWith(prefix + "_", StringComparison.OrdinalIgnoreCase)) name = name.Substring(prefix.Length + 1); var removeAccessor = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.NamingAttributeRef, attributeHelper.TheRemoveAccessorPrefixProperty, true, false, ref removeAccessor); var prefixCasing = default(Casing); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.NamingAttributeRef, attributeHelper.ThePrefixNameCasingProperty, true, false, ref prefixCasing); var removeUnderscore = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.NamingAttributeRef, attributeHelper.TheRemoveAccessorUnderscoreProperty, true, false, ref removeUnderscore); var memberCasing = default(Casing); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.NamingAttributeRef, attributeHelper.TheMemberNameCasingProperty, true, false, ref memberCasing); var str = ""; if (!removeAccessor) { str += Recase(prefix, prefixCasing); if (!removeUnderscore) str += "_"; } str += Recase(name, memberCasing); return new JST.Identifier(JST.Lexemes.StringToIdentifier(str)).ToE(); }
private void EmitEquals(Seq<JST.Statement> body, JST.Expression lhs) { var innerNameSupply = NameSupply.Fork(); var parameters = new Seq<JST.Identifier>(); parameters.Add(innerNameSupply.GenSym()); var left = parameters[0].ToE(); parameters.Add(innerNameSupply.GenSym()); var right = parameters[1].ToE(); var innerBody = new Seq<JST.Statement>(); var iequatableTypeRef = Env.Global.IEquatableTypeConstructorRef.ApplyTo(TypeCompEnv.TypeRef); var hasIEquatable = TypeCompEnv.TypeRef.IsAssignableTo(TypeCompEnv, iequatableTypeRef); var s = TypeCompEnv.Type.Style; if (s is CST.VoidTypeStyle || s is CST.ManagedPointerTypeStyle) innerBody.Add (new JST.ThrowStatement (JST.Expression.DotCall(RootId.ToE(), Constants.RootInvalidOperationException))); else if (s is CST.NumberTypeStyle || s is CST.EnumTypeStyle || TypeCompEnv.TypeRef.Equals(Env.Global.DecimalRef)) innerBody.Add(new JST.ReturnStatement(new JST.BinaryExpression(left, JST.BinaryOp.Equals, right))); else if (s is CST.HandleTypeStyle) { innerBody.Add (new JST.IfStatement (JST.Expression.IsNull(left), new JST.Statements(new JST.ReturnStatement(JST.Expression.IsNull(right))))); innerBody.Add (new JST.IfStatement (JST.Expression.IsNull(right), new JST.Statements(new JST.ReturnStatement(new JST.BooleanLiteral(false))))); innerBody.Add (new JST.ReturnStatement(new JST.BinaryExpression(left, JST.BinaryOp.StrictEquals, right))); } else if (s is CST.NullableTypeStyle) { innerBody.Add (new JST.IfStatement (JST.Expression.IsNull(left), new JST.Statements(new JST.ReturnStatement(JST.Expression.IsNull(right))))); innerBody.Add (new JST.IfStatement (JST.Expression.IsNull(right), new JST.Statements(new JST.ReturnStatement(new JST.BooleanLiteral(false))))); innerBody.Add (new JST.ReturnStatement (JST.Expression.DotCall (TypeCompEnv.ResolveType(TypeCompEnv.TypeBoundArguments[0], TypePhase.Slots), Constants.TypeEquals, left, right))); } else if (s is CST.StructTypeStyle) { if (hasIEquatable) { // Defer to IEquatable<T>::Equals var paramTypeRef = new CST.ParameterTypeRef(CST.ParameterFlavor.Type, 0); var equalsRef = new CST.MethodRef (iequatableTypeRef, "Equals", false, null, new Seq<CST.TypeRef> { paramTypeRef, paramTypeRef }, Env.Global.BooleanRef); var leftPtr = JST.Expression.DotCall (RootId.ToE(), Constants.RootNewPointerToValue, left, lhs); var call = Env.JSTHelpers.DefaultVirtualMethodCallExpression (TypeCompEnv, innerNameSupply, innerBody, equalsRef, new Seq<JST.Expression> { leftPtr, right }); innerBody.Add(new JST.ReturnStatement(call)); } else { foreach (var fieldDef in Parent.Fields.Where(f => !f.IsStatic)) { var fieldRef = new CST.FieldRef(TypeCompEnv.TypeRef, fieldDef.FieldSignature); var leftField = Env.JSTHelpers.ResolveInstanceField(TypeCompEnv, left, fieldRef); var rightField = Env.JSTHelpers.ResolveInstanceField(TypeCompEnv, right, fieldRef); innerBody.Add (new JST.IfStatement (JST.Expression.Not (JST.Expression.DotCall (TypeCompEnv.ResolveType(fieldDef.FieldType, TypePhase.Slots), Constants.TypeEquals, leftField, rightField)), new JST.Statements(new JST.ReturnStatement(new JST.BooleanLiteral(false))))); } innerBody.Add(new JST.ReturnStatement(new JST.BooleanLiteral(true))); } } else if (s is CST.ObjectTypeStyle || (s is CST.ClassTypeStyle & hasIEquatable)) { innerBody.Add (new JST.IfStatement (JST.Expression.IsNull(left), new JST.Statements(new JST.ReturnStatement(JST.Expression.IsNull(right))))); innerBody.Add (new JST.IfStatement (JST.Expression.IsNull(right), new JST.Statements(new JST.ReturnStatement(new JST.BooleanLiteral(false))))); var equalsRef = default(CST.MethodRef); if (hasIEquatable) { // Defer to IEquatable<T>::Equals var paramTypeRef = new CST.ParameterTypeRef(CST.ParameterFlavor.Type, 0); var iequatableSelfTypeRef = Env.Global.IEquatableTypeConstructorRef.ApplyTo(paramTypeRef); equalsRef = new CST.MethodRef (iequatableTypeRef, "Equals", false, null, new Seq<CST.TypeRef> { iequatableSelfTypeRef, paramTypeRef }, Env.Global.BooleanRef); } else { // Defer to Object::Equals virtual equalsRef = new CST.MethodRef (Env.Global.ObjectRef, "Equals", false, null, new Seq<CST.TypeRef> { Env.Global.ObjectRef, Env.Global.ObjectRef }, Env.Global.BooleanRef); } var call = Env.JSTHelpers.DefaultVirtualMethodCallExpression (TypeCompEnv, innerNameSupply, innerBody, equalsRef, new Seq<JST.Expression> { left, right }); innerBody.Add(new JST.ReturnStatement(call)); } else // default is ok return; body.Add (JST.Statement.DotAssignment (lhs, Constants.TypeEquals, new JST.FunctionExpression(parameters, new JST.Statements(innerBody)))); }
// Return the exported name for a getter/setter/adder/remover method based on the property/event name private JST.Expression GetterSetterAdderRemoverNameFromPropertyEvent(CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, CST.MethodDef methodDef, string prefix, JST.Expression script) { if (script != null && script is JST.FunctionExpression) throw new InvalidOperationException("not a path expression"); var names = script == null ? // Take the actual property/event name as a starting point JST.Expression.ExplodePath(RecasePropertyEvent(assemblyDef, typeDef, methodDef, null)) : // Take the user supplied property/event name as a starting point JST.Expression.ExplodePath(script); var removeAccessor = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.NamingAttributeRef, attributeHelper.TheRemoveAccessorPrefixProperty, true, false, ref removeAccessor); var prefixCasing = default(Casing); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.NamingAttributeRef, attributeHelper.ThePrefixNameCasingProperty, true, false, ref prefixCasing); var removeUnderscore = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.NamingAttributeRef, attributeHelper.TheRemoveAccessorUnderscoreProperty, true, false, ref removeUnderscore); // Turn the property/event name into a getter/setter/adder/remover name var str = ""; if (!removeAccessor) { str += Recase(prefix, prefixCasing); if (!removeUnderscore) str += "_"; } str += names[names.Count - 1].Value; names[names.Count - 1] = new JST.PropertyName(str); return JST.Expression.Path(names); }
private void BindBaseTypes(CST.TypeEnvironment thisTypeEnv, JST.Expression thisType) { if (thisTypeEnv.Type.Extends != null) { var baseTypeEnv = thisTypeEnv.Type.Extends.Enter(thisTypeEnv); var baseType = JST.Expression.Dot(thisType, Constants.TypeBaseType); if (!boundTypes.ContainsKey(baseTypeEnv.TypeRef)) boundTypes.Add(baseTypeEnv.TypeRef, new ExpressionAndPhase(baseType, TypePhase.Slots)); if (baseTypeEnv.Type.Arity > 0) { if (!boundTypes.ContainsKey(baseTypeEnv.TypeConstructorRef)) boundTypes.Add(baseTypeEnv.TypeConstructorRef, new ExpressionAndPhase(JST.Expression.Dot(baseType, Constants.TypeApplicand), TypePhase.Slots)); } BindBaseTypes(baseTypeEnv, baseType); } }
// Imports: // - instance methods: no qualification // - static methods & constructors: add qualification // Exports: // - instance methods, not prototype bound: no qualification // - instance methods, prototype bound: add qualification and 'prototype' // - static methods & constructors: add qualification private JST.Expression PrefixName(CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, CST.MethodDef methodDef, JST.Expression script, bool isExport) { if (script != null && script is JST.FunctionExpression) return script; var isNonInstance = methodDef.IsStatic || methodDef.IsConstructor; var qual = default(Qualification); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.NamingAttributeRef, attributeHelper.TheQualificationProperty, true, false, ref qual); var bindToProto = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ExportAttributeRef, attributeHelper.TheBindToPrototypeProperty, true, false, ref bindToProto); var isProto = isExport && bindToProto; var path = new Seq<JST.PropertyName>(); if (script == null && !methodDef.IsStatic && methodDef.IsConstructor && qual == Qualification.None) qual = Qualification.Type; if (!isExport && !isNonInstance && qual != Qualification.None) qual = Qualification.None; if (isExport && !isNonInstance && !isProto && qual != Qualification.None) qual = Qualification.None; if (isExport && !isNonInstance && isProto && qual == Qualification.None) qual = Qualification.Type; if (isNonInstance) { var global = default(JST.Expression); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.NamingAttributeRef, attributeHelper.TheGlobalObjectProperty, true, false, ref global); if (global != null) { if (global is JST.FunctionExpression) { var ctxt = CST.MessageContextBuilders.Member(env.Global, assemblyDef, typeDef, methodDef); env.Log(new InvalidInteropMessage(ctxt, "global object expression cannot be a function")); throw new DefinitionException(); } foreach (var p in JST.Expression.ExplodePath(global)) path.Add(p); } } if (qual == Qualification.Full) { var nm = typeDef.EffectiveName(env.Global); if (nm.Namespace.Length > 0) { var nsCasing = default(Casing); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.NamingAttributeRef, attributeHelper.TheNamespaceCasingProperty, true, false, ref nsCasing); foreach (var n in nm.Namespace.Split('.')) path.Add(new JST.PropertyName(Recase(n, nsCasing))); } } if (qual == Qualification.Full || qual == Qualification.Type) { var tnCasing = default(Casing); attributeHelper.GetValueFromType (assemblyDef, typeDef, attributeHelper.NamingAttributeRef, attributeHelper.TheTypeNameCasingProperty, true, false, ref tnCasing); foreach (var n in typeDef.EffectiveName(env.Global).Types.Select (name => new JST.PropertyName(Recase(name, tnCasing)))) path.Add(n); } if (isProto) path.Add(new JST.PropertyName(Constants.prototype)); if (script != null) { foreach (var p in JST.Expression.ExplodePath(script)) path.Add(p); } return JST.Expression.Path(path); }
public ExpressionAndPhase(JST.Expression expression, TypePhase phase) { Expression = expression; Phase = phase; }
private JST.Expression RecaseMethod(CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, CST.MethodDef methodDef, JST.Expression script) { if (script != null) return script; var casing = default(Casing); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.NamingAttributeRef, attributeHelper.TheMemberNameCasingProperty, true, false, ref casing); return new JST.Identifier(JST.Lexemes.StringToIdentifier(Recase(methodDef.Name, casing))).ToE(); }
// ---------------------------------------------------------------------- // Methods // ---------------------------------------------------------------------- public JST.Expression MethodCallExpression(CST.MethodRef methodRef, JST.NameSupply localNameSupply, bool isFactory, IImSeq<JST.Expression> arguments) { return env.JSTHelpers.DefaultMethodCallExpression(this, localNameSupply, methodRef, isFactory, arguments); }
// Compile all methods not already compiled by above private void CompileMethods(Seq<JST.Statement> body, JST.NameSupply outerNameSupply) { switch (Env.CompilationMode) { case CompilationMode.Plain: case CompilationMode.Collecting: // Already compiled above return; case CompilationMode.Traced: foreach (var methodDef in Methods.Where(d => TypeTrace.Methods.Contains(d.MethodSignature))) { if (TypeTrace.Parent.Parent.Flavor == TraceFlavor.Remainder) { // Compile method into stand-alone loader var compiler = new MethodCompiler(this, outerNameSupply, methodDef, MethodCompilationMode.SelfContained); compiler.Emit(body, null); } else { // Bind method definition into method cache var target = TypeDefinitionId.ToE(); if (TyconEnv.Type.Arity == 0 && !Env.InteropManager.IsStatic(TyconEnv.Assembly, TyconEnv.Type, methodDef)) target = JST.Expression.Dot (target, Constants.TypeConstructObject, Constants.prototype); target = JST.Expression.Dot(target, Constants.TypeMethodCache); var compiler = new MethodCompiler(this, outerNameSupply, methodDef, MethodCompilationMode.DirectBind); compiler.Emit(body, target); } } break; default: throw new ArgumentOutOfRangeException(); } }
private void EmitSetupType(Seq<JST.Statement> body, JST.Expression lhs) { // NOTE: We always emit the SetupType function, even if the body is empty, so that we can // track which types are at phase 3. (We need to initialize base types and type arguments // to phase 3 when type itself is brought up to phase 3.) // Collect static fields and their types // NOTE: We used to also bind null instance fields into the prototype, but this turns out to // be a significant performance hit var staticFields = new Seq<CST.FieldRef>(); var usage = new CST.Usage(); foreach (var fieldDef in Parent.Fields.Where(f => f.IsStatic)) { var fieldRef = new CST.FieldRef(TypeCompEnv.TypeRef, fieldDef.FieldSignature); if (TypeCompEnv.Type.Style is CST.EnumTypeStyle || fieldDef.Init == null || fieldDef.Init.Flavor != CST.FieldInitFlavor.Const) { staticFields.Add(fieldRef); if (Env.JSTHelpers.DefaultFieldValueIsNonNull(TypeCompEnv, fieldRef)) // We'll need type to construct default fieldRef.ExternalFieldType.AccumUsage(usage, true); } // else: constant static fields, other than enums, don't need any run-time representation } var innerBody = new Seq<JST.Statement>(); var innerTypeCompEnv = TypeCompEnv.EnterFunction(); innerTypeCompEnv.BindUsage(innerBody, usage, TypePhase.Constructed); if (staticFields.Count > 0) { if (Env.DebugMode) innerBody.Add(new JST.CommentStatement("Static fields")); foreach (var fieldRef in staticFields) { innerBody.Add (JST.Statement.DotAssignment (TypeId.ToE(), Env.JSTHelpers.ResolveFieldToIdentifier(innerTypeCompEnv, fieldRef, true), Env.JSTHelpers.DefaultFieldValue(innerTypeCompEnv, fieldRef))); } } if (Parent.StaticInitializer != null) { if (Env.DebugMode) innerBody.Add(new JST.CommentStatement("Static constructor")); innerBody.Add (new JST.ExpressionStatement (innerTypeCompEnv.MethodCallExpression (Parent.StaticInitializer, innerTypeCompEnv.NameSupply, false, JST.Constants.EmptyExpressions))); } EmitReflection(innerBody, innerTypeCompEnv, TypeId.ToE()); body.Add (JST.Statement.DotAssignment (lhs, Constants.TypeSetupType, new JST.FunctionExpression(null, new JST.Statements(innerBody)))); }